[llvm-branch-commits] [clang] [SSAF][PointerFlow] Add PointerFlow summary and extractor (PR #188654)
Ziqing Luo via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Apr 16 13:53:00 PDT 2026
https://github.com/ziqingluo-90 updated https://github.com/llvm/llvm-project/pull/188654
>From cb8c65a3e04198b27b9706d52b4dd4a99478a240 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 12 Mar 2026 14:10:19 -0700
Subject: [PATCH 01/51] Reapply "[clang][ssaf] Add UnsafeBufferUsage summary
extractor for functions (#182941)"
This reverts commit 53739c75a8720aaef8032628267ed4fd050af038.
Reapply after module dependency issues are resolved.
(rdar://169191570)
---
.../UnsafeBufferUsage/UnsafeBufferUsage.h | 56 +--
.../UnsafeBufferUsageExtractor.h | 40 ++
.../UnsafeBufferUsageBuilder.h | 32 --
.../Analyses/CMakeLists.txt | 15 +
.../UnsafeBufferUsageExtractor.cpp | 281 ++++++++++++
.../CMakeLists.txt | 2 +
.../UnsafeBufferUsageTest.cpp | 423 ++++++++++++++++--
.../CMakeLists.txt | 1 +
8 files changed, 743 insertions(+), 107 deletions(-)
rename clang/include/clang/ScalableStaticAnalysisFramework/{Core => }/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h (62%)
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
delete mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
similarity index 62%
rename from clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
rename to clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 5b58ed0684333..5f418000723b4 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -6,13 +6,12 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
+#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/Core/Model/SummaryName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
-#include "llvm/ADT/iterator_range.h"
#include <set>
namespace clang::ssaf {
@@ -26,23 +25,19 @@ namespace clang::ssaf {
/// *' 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');
-/// - 'P.EntityId' identifies an lvalue object and 'P.PointerLevel == 0'.
-/// The latter case represents address-of expressions.
+/// 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, 1)' is associated with 'int *[10]' of 'p';
-/// '(p, 2)' is associated with 'int *' of 'p';
-/// '(p, 0)' represents '&p'.
+/// - '(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 UnsafeBufferUsageTUSummaryBuilder;
- friend class UnsafeBufferUsageEntitySummary;
+ friend class UnsafeBufferUsageTUSummaryExtractor;
EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
: Entity(Entity), PointerLevel(PointerLevel) {}
@@ -52,7 +47,8 @@ class EntityPointerLevel {
unsigned getPointerLevel() const { return PointerLevel; }
bool operator==(const EntityPointerLevel &Other) const {
- return Entity == Other.Entity && PointerLevel == Other.PointerLevel;
+ return std::tie(Entity, PointerLevel) ==
+ std::tie(Other.Entity, Other.PointerLevel);
}
bool operator!=(const EntityPointerLevel &Other) const {
@@ -64,7 +60,8 @@ class EntityPointerLevel {
std::tie(Other.Entity, Other.PointerLevel);
}
- // Comparator supporting partial comparison against EntityId:
+ /// Compares `EntityPointerLevel`s; additionally, partially compares
+ /// `EntityPointerLevel` with `EntityId`.
struct Comparator {
using is_transparent = void;
bool operator()(const EntityPointerLevel &L,
@@ -88,33 +85,20 @@ using EntityPointerLevelSet =
class UnsafeBufferUsageEntitySummary final : public EntitySummary {
const EntityPointerLevelSet UnsafeBuffers;
- friend class UnsafeBufferUsageTUSummaryBuilder;
+ friend class UnsafeBufferUsageTUSummaryExtractor;
- UnsafeBufferUsageEntitySummary(EntityPointerLevelSet &&UnsafeBuffers)
+ UnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers)
: EntitySummary(), UnsafeBuffers(std::move(UnsafeBuffers)) {}
public:
- using const_iterator = EntityPointerLevelSet::const_iterator;
-
- const_iterator begin() const { return UnsafeBuffers.begin(); }
- const_iterator end() const { return UnsafeBuffers.end(); }
-
- const_iterator find(const EntityPointerLevel &V) const {
- return UnsafeBuffers.find(V);
- }
-
- llvm::iterator_range<const_iterator> getSubsetOf(EntityId Entity) const {
- return llvm::make_range(UnsafeBuffers.equal_range(Entity));
- }
-
- /// \return the size of the set of EntityLevelPointers, which represents the
- /// set of unsafe buffers
- size_t getNumUnsafeBuffers() { return UnsafeBuffers.size(); }
-
SummaryName getSummaryName() const override {
return SummaryName{"UnsafeBufferUsage"};
};
+
+ bool operator==(const EntityPointerLevelSet &Other) const {
+ return UnsafeBuffers == Other;
+ }
};
} // namespace clang::ssaf
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
new file mode 100644
index 0000000000000..765b2c37562ce
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
@@ -0,0 +1,40 @@
+//===- UnsafeBufferUsageExtractor.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
+
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
+#include "llvm/Support/Error.h"
+#include <memory>
+
+namespace clang::ssaf {
+class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor {
+public:
+ UnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder)
+ : TUSummaryExtractor(Builder) {}
+
+ static EntityPointerLevel buildEntityPointerLevel(EntityId Entity,
+ unsigned PointerLevel) {
+ return {Entity, PointerLevel};
+ }
+
+ EntityId addEntity(EntityName EN) { return SummaryBuilder.addEntity(EN); }
+
+ std::unique_ptr<UnsafeBufferUsageEntitySummary>
+ extractEntitySummary(const Decl *Contributor, ASTContext &Ctx,
+ llvm::Error &Error);
+
+ void HandleTranslationUnit(ASTContext &Ctx) override;
+};
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h
deleted file mode 100644
index db6c097510a57..0000000000000
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h
+++ /dev/null
@@ -1,32 +0,0 @@
-//===- UnsafeBufferUsageBuilder.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_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
-
-#include "clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
-#include <memory>
-
-namespace clang::ssaf {
-class UnsafeBufferUsageTUSummaryBuilder : public TUSummaryBuilder {
-public:
- static EntityPointerLevel buildEntityPointerLevel(EntityId Entity,
- unsigned PointerLevel) {
- return {Entity, PointerLevel};
- }
-
- static std::unique_ptr<UnsafeBufferUsageEntitySummary>
- buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet &&UnsafeBuffers) {
- return std::make_unique<UnsafeBufferUsageEntitySummary>(
- UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers)));
- }
-};
-} // namespace clang::ssaf
-
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
new file mode 100644
index 0000000000000..34c6dd9b61203
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses
+ UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+
+ LINK_LIBS
+ clangAST
+ clangAnalysis
+ clangBasic
+ clangScalableStaticAnalysisFrameworkCore
+
+ DEPENDS
+ )
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
new file mode 100644
index 0000000000000..c150b2918db59
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -0,0 +1,281 @@
+//===- UnsafeBufferUsageExtractor.cpp -------------------------------------===//
+//
+// 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/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Error.h"
+#include <memory>
+
+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;
+
+// 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 llvm::createStringError(
+ "unsupported expression kind for translation to "
+ "EntityPointerLevel: %s",
+ E->getStmtClassName());
+ }
+
+ 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;
+
+public:
+ EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor)
+ : Extractor(Extractor) {}
+
+ 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 llvm::createStringError(
+ "failed to create entity name from the Decl of " +
+ E->getNameInfo().getAsString());
+ }
+
+ // Translate({., ->}f) -> {(MemberDecl, 1)}
+ Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
+ if (auto EntityName = getEntityName(E->getMemberDecl()))
+ return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
+ return llvm::createStringError(
+ "failed to create entity name from the MemberDecl of " +
+ E->getMemberDecl()->getNameAsString());
+ }
+
+ Expected<EntityPointerLevelSet>
+ VisitOpaqueValueExpr(const OpaqueValueExpr *S) {
+ return Visit(S->getSourceExpr());
+ }
+};
+
+Expected<EntityPointerLevelSet>
+buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
+ UnsafeBufferUsageTUSummaryExtractor &Extractor) {
+ EntityPointerLevelSet Result{};
+ EntityPointerLevelTranslator Translator{Extractor};
+ llvm::Error AllErrors = llvm::ErrorSuccess();
+
+ for (const Expr *Ptr : UnsafePointers) {
+ Expected<EntityPointerLevelSet> Translation = Translator.translate(Ptr);
+
+ if (Translation) {
+ // Filter out those temporary invalid EntityPointerLevels associated with
+ // `&E` pointers:
+ auto FilteredTranslation = llvm::make_filter_range(
+ *Translation, [](const EntityPointerLevel &E) -> bool {
+ return E.getPointerLevel() > 0;
+ });
+ Result.insert(FilteredTranslation.begin(), FilteredTranslation.end());
+ continue;
+ }
+ AllErrors = llvm::joinErrors(std::move(AllErrors), Translation.takeError());
+ }
+ if (AllErrors)
+ return AllErrors;
+ return Result;
+}
+} // namespace
+
+std::unique_ptr<UnsafeBufferUsageEntitySummary>
+UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary(
+ const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) {
+ // FIXME: findUnsafePointers should accept more kinds of `Decl`s than just
+ // `FunctionDecl`:
+ if (const auto *FD = dyn_cast<FunctionDecl>(Contributor)) {
+ Expected<EntityPointerLevelSet> EPLs =
+ buildEntityPointerLevels(findUnsafePointers(FD), *this);
+
+ if (EPLs)
+ return std::make_unique<UnsafeBufferUsageEntitySummary>(
+ UnsafeBufferUsageEntitySummary(std::move(*EPLs)));
+ Error = EPLs.takeError();
+ }
+ return nullptr;
+}
+
+void UnsafeBufferUsageTUSummaryExtractor::HandleTranslationUnit(
+ ASTContext &Ctx) {
+ // FIXME: impl me!
+}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
index d3d75430233fe..6bcb1a0038127 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -1,2 +1,4 @@
+add_subdirectory(Analyses)
add_subdirectory(Core)
add_subdirectory(Frontend)
+
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 082ff7f8ac175..81c742dc30811 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -6,37 +6,144 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
using namespace clang;
using namespace ssaf;
+using testing::UnorderedElementsAre;
namespace {
+template <typename SomeDecl = NamedDecl>
+const SomeDecl *findDeclByName(StringRef Name, ASTContext &Ctx) {
+ class NamedDeclFinder : public DynamicRecursiveASTVisitor {
+ public:
+ StringRef SearchingName;
+ const NamedDecl *FoundDecl = nullptr;
+
+ NamedDeclFinder(StringRef SearchingName) : SearchingName(SearchingName) {}
+
+ bool VisitDecl(Decl *D) override {
+ if (const auto *ND = dyn_cast<NamedDecl>(D)) {
+ if (ND->getNameAsString() == SearchingName) {
+ FoundDecl = ND;
+ return false;
+ }
+ }
+ return true;
+ }
+ };
+
+ NamedDeclFinder Finder(Name);
+
+ Finder.TraverseDecl(Ctx.getTranslationUnitDecl());
+ return dyn_cast_or_null<SomeDecl>(Finder.FoundDecl);
+}
+
+const FunctionDecl *findFnByName(StringRef Name, ASTContext &Ctx) {
+ return findDeclByName<FunctionDecl>(Name, Ctx);
+}
+
constexpr inline auto buildEntityPointerLevel =
- UnsafeBufferUsageTUSummaryBuilder::buildEntityPointerLevel;
-constexpr inline auto buildUnsafeBufferUsageEntitySummary =
- UnsafeBufferUsageTUSummaryBuilder::buildUnsafeBufferUsageEntitySummary;
+ UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
class UnsafeBufferUsageTest : public testing::Test {
protected:
- EntityIdTable Table;
+ TUSummary TUSum;
+ TUSummaryBuilder TUSummaryBuilder;
+ UnsafeBufferUsageTUSummaryExtractor Extractor;
+ std::unique_ptr<ASTUnit> AST;
+
+ UnsafeBufferUsageTest()
+ : TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
+ TUSummaryBuilder(TUSum), Extractor(TUSummaryBuilder) {}
+
+ std::unique_ptr<UnsafeBufferUsageEntitySummary>
+ setUpTest(StringRef Code, StringRef ContributorName) {
+ AST = tooling::buildASTFromCodeWithArgs(
+ Code, {"-Wno-unused-value -Wno-int-to-pointer-cast"});
+
+ const auto *ContributorDefn =
+ findDeclByName(ContributorName, AST->getASTContext());
+ std::optional<EntityName> EN = getEntityName(ContributorDefn);
+
+ if (!ContributorDefn || !EN)
+ return nullptr;
+
+ llvm::Error Error = llvm::ErrorSuccess();
+ auto Sum = Extractor.extractEntitySummary(ContributorDefn,
+ AST->getASTContext(), Error);
+
+ if (Error) {
+ llvm::consumeError(std::move(Error));
+ return nullptr;
+ }
+ return Sum;
+ }
+
+ std::optional<EntityId> getEntityId(StringRef Name) {
+ if (const auto *D = findDeclByName(Name, AST->getASTContext()))
+ if (auto EntityName = getEntityName(D))
+ return Extractor.addEntity(*EntityName);
+ return std::nullopt;
+ }
+
+ std::optional<EntityId> getEntityIdForReturn(StringRef FunName) {
+ if (const auto *D = findFnByName(FunName, AST->getASTContext()))
+ if (auto EntityName = getEntityNameForReturn(D))
+ return Extractor.addEntity(*EntityName);
+ return std::nullopt;
+ }
+
+ // Same as `std::pair<StringName, unsigned>` for a pair of entity declaration
+ // name and a pointer level with an extra optional flag for whether the entity
+ // represents a function return value:
+ struct EPLPair {
+ EPLPair(StringRef Name, unsigned Lv, bool isFunRet = false)
+ : Name(Name), Lv(Lv), isFunRet(isFunRet) {}
+
+ StringRef Name;
+ unsigned Lv;
+ bool isFunRet;
+ };
+
+ EntityPointerLevelSet makeSet(unsigned Line, ArrayRef<EPLPair> Pairs) {
+ auto EPLs = llvm::map_range(
+ Pairs, [this, Line](const EPLPair &Pair) -> EntityPointerLevel {
+ std::optional<EntityId> Entity = Pair.isFunRet
+ ? getEntityIdForReturn(Pair.Name)
+ : getEntityId(Pair.Name);
+ if (!Entity)
+ ADD_FAILURE_AT(__FILE__, Line) << "Entity not found: " << Pair.Name;
+ return buildEntityPointerLevel(*Entity, Pair.Lv);
+ });
+ return EntityPointerLevelSet{EPLs.begin(), EPLs.end()};
+ }
};
//////////////////////////////////////////////////////////////
// Data Structure Tests //
//////////////////////////////////////////////////////////////
-#define EXPECT_CONTAINS(Set, Elt) EXPECT_NE((Set).find(Elt), (Set).end())
-#define EXPECT_EXCLUDES(Set, Elt) EXPECT_EQ((Set).find(Elt), (Set).end())
+static llvm::iterator_range<EntityPointerLevelSet::iterator>
+getSubsetOf(const EntityPointerLevelSet &Set, EntityId Entity) {
+ return llvm::make_range(Set.equal_range(Entity));
+}
TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
- EntityId E1 = Table.getId({"c:@F at foo", "", {}});
- EntityId E2 = Table.getId({"c:@F at bar", "", {}});
+ EntityId E1 = Extractor.addEntity({"c:@F at foo", "", {}});
+ EntityId E2 = Extractor.addEntity({"c:@F at bar", "", {}});
auto P1 = buildEntityPointerLevel(E1, 2);
auto P2 = buildEntityPointerLevel(E1, 2);
@@ -53,48 +160,286 @@ TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
EXPECT_FALSE(P2 < P1);
}
-TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntitySummaryTest) {
- EntityId E1 = Table.getId({"c:@F at foo", "", {}});
- EntityId E2 = Table.getId({"c:@F at bar", "", {}});
- EntityId E3 = Table.getId({"c:@F at baz", "", {}});
+TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
+ EntityId E1 = Extractor.addEntity({"c:@F at foo", "", {}});
+ EntityId E2 = Extractor.addEntity({"c:@F at bar", "", {}});
+ EntityId E3 = Extractor.addEntity({"c:@F at baz", "", {}});
auto P1 = buildEntityPointerLevel(E1, 1);
auto P2 = buildEntityPointerLevel(E1, 2);
auto P3 = buildEntityPointerLevel(E2, 1);
auto P4 = buildEntityPointerLevel(E2, 2);
auto P5 = buildEntityPointerLevel(E3, 1);
- auto P6 = buildEntityPointerLevel(E3, 2);
EntityPointerLevelSet Set{P1, P2, P3, P4, P5};
- auto ES = buildUnsafeBufferUsageEntitySummary(std::move(Set));
- ASSERT_TRUE(ES);
- EXPECT_CONTAINS(*ES, P1);
- EXPECT_CONTAINS(*ES, P2);
- EXPECT_CONTAINS(*ES, P3);
- EXPECT_CONTAINS(*ES, P4);
- EXPECT_CONTAINS(*ES, P5);
- EXPECT_EXCLUDES(*ES, P6);
+ EXPECT_THAT(Set, UnorderedElementsAre(P1, P2, P3, P4, P5));
+ EXPECT_THAT(getSubsetOf(Set, E1), UnorderedElementsAre(P1, P2));
+ EXPECT_THAT(getSubsetOf(Set, E2), UnorderedElementsAre(P3, P4));
+ EXPECT_THAT(getSubsetOf(Set, E3), UnorderedElementsAre(P5));
+}
+
+//////////////////////////////////////////////////////////////
+// Extractor Tests //
+//////////////////////////////////////////////////////////////
+
+TEST_F(UnsafeBufferUsageTest, SimpleFunctionWithUnsafePointer) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p) {
+ p[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
- EntityPointerLevelSet Subset1{ES->getSubsetOf(E1).begin(),
- ES->getSubsetOf(E1).end()};
+TEST_F(UnsafeBufferUsageTest, PointerArithmetic) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q) {
+ *(p + 5);
+ *(q - 3);
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, PointerIncrementDecrement) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q, int *r, int *s) {
+ (++p)[5];
+ (q++)[5];
+ (--r)[5];
+ (s--)[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum,
+ makeSet(__LINE__, {{"p", 1U}, {"q", 1U}, {"r", 1U}, {"s", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, PointerAssignment) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q) {
+ (p = q + 5)[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, CompoundAssignment) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q) {
+ (p += 5)[5];
+ (q -= 3)[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
+}
- EXPECT_CONTAINS(Subset1, P1);
- EXPECT_CONTAINS(Subset1, P2);
- EXPECT_EQ(Subset1.size(), 2U);
+TEST_F(UnsafeBufferUsageTest, MultiLevelPointer) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int **p, int **q, int **r) {
+ (*p)[5];
+ *(*q);
+ *(q[5]);
+ r[5][5];
+ }
+ )cpp",
+ "foo");
- EntityPointerLevelSet Subset2{ES->getSubsetOf(E2).begin(),
- ES->getSubsetOf(E2).end()};
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum,
+ makeSet(__LINE__, {{"p", 2U}, {"q", 1U}, {"r", 1U}, {"r", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, ConditionalOperator) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int **p, int **q, int cond) {
+ (cond ? *p : *q)[5];
+ cond ? p[5] : q[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum,
+ makeSet(__LINE__, {{"p", 1U}, {"q", 1U}, {"p", 2U}, {"q", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, CastExpression) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(void *p, int q) {
+ ((int*)p)[5];
+ ((int*)q)[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, CommaOperator) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int x) {
+ (x++, p)[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, CommaOperator2) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int **p, int **q, int x) {
+ (p[x] = 0, q[x] = 0)[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}, {"q", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, ParenthesizedExpression) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p) {
+ (((p)))[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, ArrayParameter) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int arr[], int arr2[][10]) {
+ int n = 5;
+ arr[100];
+ arr2[5][n];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"arr", 1U}, {"arr2", 1U}, {"arr2", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, FunctionCall) {
+ auto Sum = setUpTest(R"cpp(
+ int ** (*fp)();
+ int ** foo() {
+ fp = &foo;
+ foo()[5];
+ (*fp())[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ // No (foo, 2) becasue indirect calls are ignored.
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"foo", 1U, true}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, StructMemberAccess) {
+ auto Sum = setUpTest(R"cpp(
+ struct S {
+ int *ptr;
+ int (*ptr_to_arr)[10];
+ };
+ void foo(struct S obj) {
+ int n = 5;
+ obj.ptr[5];
+ (*obj.ptr_to_arr)[n];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}, {"ptr_to_arr", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, StringLiteralSubscript) {
+ auto Sum = setUpTest(R"cpp(
+ void foo() {
+ "hello"[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ // String literals should not generate pointer kind variables
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {}));
+}
+
+TEST_F(UnsafeBufferUsageTest, OpaqueValueExpr) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q) {
+ (p ?: q)[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, AddressOfOperator) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int x) {
+ (&x)[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ // Address-of should not generate pointer kind variables for 'x'
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {}));
+}
+
+TEST_F(UnsafeBufferUsageTest, AddressOfThenDereference) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q) {
+ (*(&p))[5];
+ (&(*q))[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1}, {"q", 1}}));
+}
- EXPECT_CONTAINS(Subset2, P3);
- EXPECT_CONTAINS(Subset2, P4);
- EXPECT_EQ(Subset2.size(), 2U);
+TEST_F(UnsafeBufferUsageTest, PointerToArrayOfPointers) {
+ auto Sum = setUpTest(R"cpp(
+ void foo() {
+ int * arr[10];
+ int * (*p)[10] = arr;
- EntityPointerLevelSet Subset3{ES->getSubsetOf(E3).begin(),
- ES->getSubsetOf(E3).end()};
+ (*p)[5][5]; // '(*p)[5]' is unsafe
+ // '(*p)' is fine because 5 < 10
+ }
+ )cpp",
+ "foo");
- EXPECT_CONTAINS(Subset3, P5);
- EXPECT_EXCLUDES(Subset3, P6);
- EXPECT_EQ(Subset3.size(), 1U);
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 3}}));
}
} // namespace
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
index 871d9e6b0c02c..f541e81d42789 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -29,6 +29,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
clangASTMatchers
clangBasic
clangFrontend
+ clangScalableStaticAnalysisFrameworkAnalyses
clangScalableStaticAnalysisFrameworkCore
clangScalableStaticAnalysisFrameworkFrontend
clangSerialization
>From 09fcf76d7f0e854bcb2fc3521e4692d0f1974efd Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 5 Mar 2026 13:00:44 -0800
Subject: [PATCH 02/51] [ssaf][UnsafeBufferUsage] Add support for extracting
unsafe pointers from all kinds of contributors
- Generalize the -Wunsafe-buffer-usage API for finding unsafe pointers in all kinds of Decls
- Add support in SSAF-based UnsafeBufferUsage analysis for extracting from various contributors
- Mock implementation of HandleTranslationUnit
rdar://171735836
---
.../Analysis/Analyses/UnsafeBufferUsage.h | 9 +-
.../UnsafeBufferUsage/UnsafeBufferUsage.h | 2 +
clang/lib/Analysis/UnsafeBufferUsage.cpp | 61 +++++---
.../UnsafeBufferUsageExtractor.cpp | 140 ++++++++++++++----
.../UnsafeBufferUsageTest.cpp | 62 +++++++-
5 files changed, 226 insertions(+), 48 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 876682ad779d4..e0d583c735e61 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -201,7 +201,14 @@ bool anyConflict(const llvm::SmallVectorImpl<FixItHint> &FixIts,
const SourceManager &SM);
} // namespace internal
-std::set<const Expr *> findUnsafePointers(const FunctionDecl *FD);
+/// Find unsafe pointers in body/initializer of `D`, if `D` is one of the
+/// followings:
+/// VarDecl
+/// FieldDecl
+/// FunctionDecl
+/// BlockDecl
+/// ObjCMethodDecl
+std::set<const Expr *> findUnsafePointers(const Decl *D);
} // end namespace clang
#endif /* LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H */
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 5f418000723b4..c0a4c2f76ab48 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -98,6 +98,8 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
bool operator==(const EntityPointerLevelSet &Other) const {
return UnsafeBuffers == Other;
}
+
+ bool empty() const { return UnsafeBuffers.empty(); }
};
} // namespace clang::ssaf
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 133e39b8fac2b..d6f554246a1b4 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -28,6 +28,7 @@
#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
@@ -36,6 +37,7 @@
#include <queue>
#include <set>
#include <sstream>
+#include <vector>
using namespace clang;
@@ -2942,7 +2944,37 @@ template <typename NodeTy> struct CompareNode {
}
};
-std::set<const Expr *> clang::findUnsafePointers(const FunctionDecl *FD) {
+// Populate `Stmts` with the body/initializer Stmt of `D`, if `D` is one of the
+// followings:
+// VarDecl
+// FieldDecl
+// FunctionDecl
+// BlockDecl
+// ObjCMethodDecl
+static void populateStmtsForFindingGadgets(SmallVector<const Stmt *> &Stmts,
+ const Decl *D) {
+ auto AddStmt = [&Stmts](const Stmt *S) {
+ if (S)
+ Stmts.push_back(S);
+ };
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ AddStmt(FD->getBody());
+ for (const auto *PD : FD->parameters())
+ AddStmt(PD->getDefaultArg());
+ if (const auto *CtorD = dyn_cast<CXXConstructorDecl>(FD))
+ llvm::append_range(
+ Stmts, llvm::map_range(CtorD->inits(),
+ std::mem_fn(&CXXCtorInitializer::getInit)));
+ } else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
+ AddStmt(D->getBody());
+ } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
+ AddStmt(VD->getInit()); // FIXME: default arg for ParmVarDecl?
+ } else if (const auto *FD = dyn_cast<FieldDecl>(D)) {
+ AddStmt(FD->getInClassInitializer());
+ }
+}
+
+std::set<const Expr *> clang::findUnsafePointers(const Decl *D) {
class MockReporter : public UnsafeBufferUsageHandler {
public:
MockReporter() {}
@@ -2981,9 +3013,13 @@ std::set<const Expr *> clang::findUnsafePointers(const FunctionDecl *FD) {
WarningGadgetList WarningGadgets;
DeclUseTracker Tracker;
MockReporter IgnoreHandler;
+ ASTContext &Ctx = D->getASTContext();
+ SmallVector<const Stmt *> Stmts;
- findGadgets(FD->getBody(), FD->getASTContext(), IgnoreHandler, false,
- FixableGadgets, WarningGadgets, Tracker);
+ populateStmtsForFindingGadgets(Stmts, D);
+ for (auto *Stmt : Stmts)
+ findGadgets(Stmt, Ctx, IgnoreHandler, false, FixableGadgets, WarningGadgets,
+ Tracker);
std::set<const Expr *> Result;
for (auto &G : WarningGadgets) {
@@ -4673,9 +4709,6 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
#endif
assert(D);
-
- SmallVector<Stmt *> Stmts;
-
if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
// Consteval functions are free of UB by the spec, so we don't need to
// visit them or produce diagnostics.
@@ -4697,27 +4730,21 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
break;
}
}
+ }
- Stmts.push_back(FD->getBody());
+ SmallVector<const Stmt *> Stmts;
- if (const auto *ID = dyn_cast<CXXConstructorDecl>(D)) {
- for (const CXXCtorInitializer *CI : ID->inits()) {
- Stmts.push_back(CI->getInit());
- }
- }
- } else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
- Stmts.push_back(D->getBody());
- }
+ populateStmtsForFindingGadgets(Stmts, D);
assert(!Stmts.empty());
FixableGadgetList FixableGadgets;
WarningGadgetList WarningGadgets;
DeclUseTracker Tracker;
- for (Stmt *S : Stmts) {
+ for (const Stmt *S : Stmts) {
findGadgets(S, D->getASTContext(), Handler, EmitSuggestions, FixableGadgets,
WarningGadgets, Tracker);
}
applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
std::move(Tracker), Handler, EmitSuggestions);
-}
+}
\ No newline at end of file
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index c150b2918db59..c609168e4dc7d 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -33,6 +33,32 @@ static bool hasPointerType(const Expr *E) {
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(
+ Ctx.getSourceManager());
+ return llvm::createStringError(
+ "failed to create entity name for %s declared at %s",
+ FailedDecl->getNameAsString().c_str(), LocStr.c_str());
+}
+
+static llvm::Error makeAddEntitySummaryError(const NamedDecl *FailedContributor,
+ ASTContext &Ctx) {
+ std::string LocStr =
+ FailedContributor->getSourceRange().getBegin().printToString(
+ Ctx.getSourceManager());
+ return llvm::createStringError(
+ "failed to add entity summary for contributor %s declared at %s",
+ FailedContributor->getNameAsString().c_str(), LocStr.c_str());
+}
+
// 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
@@ -59,10 +85,7 @@ class EntityPointerLevelTranslator
// Fallback method for all unsupported expression kind:
llvm::Error fallback(const Stmt *E) {
- return llvm::createStringError(
- "unsupported expression kind for translation to "
- "EntityPointerLevel: %s",
- E->getStmtClassName());
+ return makeUnsupportedStmtKindError(E);
}
static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
@@ -92,10 +115,12 @@ class EntityPointerLevelTranslator
}
UnsafeBufferUsageTUSummaryExtractor &Extractor;
+ ASTContext &Ctx;
public:
- EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor)
- : Extractor(Extractor) {}
+ EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor,
+ ASTContext &Ctx)
+ : Extractor(Extractor), Ctx(Ctx) {}
Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
@@ -210,18 +235,14 @@ class EntityPointerLevelTranslator
Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
if (auto EntityName = getEntityName(E->getDecl()))
return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
- return llvm::createStringError(
- "failed to create entity name from the Decl of " +
- E->getNameInfo().getAsString());
+ 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 llvm::createStringError(
- "failed to create entity name from the MemberDecl of " +
- E->getMemberDecl()->getNameAsString());
+ return makeCreateEntityNameError(E->getMemberDecl(), Ctx);
}
Expected<EntityPointerLevelSet>
@@ -232,9 +253,10 @@ class EntityPointerLevelTranslator
Expected<EntityPointerLevelSet>
buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
- UnsafeBufferUsageTUSummaryExtractor &Extractor) {
+ UnsafeBufferUsageTUSummaryExtractor &Extractor,
+ ASTContext &Ctx) {
EntityPointerLevelSet Result{};
- EntityPointerLevelTranslator Translator{Extractor};
+ EntityPointerLevelTranslator Translator{Extractor, Ctx};
llvm::Error AllErrors = llvm::ErrorSuccess();
for (const Expr *Ptr : UnsafePointers) {
@@ -258,24 +280,90 @@ buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
}
} // namespace
+static std::set<const Expr *> findUnsafePointersInContributor(const Decl *D) {
+ if (isa<FunctionDecl>(D) || isa<VarDecl>(D))
+ return findUnsafePointers(D);
+ if (auto *RD = dyn_cast<RecordDecl>(D)) {
+ std::set<const Expr *> Result;
+
+ for (const FieldDecl *FD : RD->fields()) {
+ Result.merge(findUnsafePointers(FD));
+ }
+ return Result;
+ }
+ return {};
+}
+
std::unique_ptr<UnsafeBufferUsageEntitySummary>
UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary(
const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) {
- // FIXME: findUnsafePointers should accept more kinds of `Decl`s than just
- // `FunctionDecl`:
- if (const auto *FD = dyn_cast<FunctionDecl>(Contributor)) {
- Expected<EntityPointerLevelSet> EPLs =
- buildEntityPointerLevels(findUnsafePointers(FD), *this);
-
- if (EPLs)
- return std::make_unique<UnsafeBufferUsageEntitySummary>(
- UnsafeBufferUsageEntitySummary(std::move(*EPLs)));
- Error = EPLs.takeError();
- }
+ Expected<EntityPointerLevelSet> EPLs = buildEntityPointerLevels(
+ findUnsafePointersInContributor(Contributor), *this, Ctx);
+
+ if (EPLs)
+ return std::make_unique<UnsafeBufferUsageEntitySummary>(
+ UnsafeBufferUsageEntitySummary(std::move(*EPLs)));
+ Error = EPLs.takeError();
return nullptr;
}
void UnsafeBufferUsageTUSummaryExtractor::HandleTranslationUnit(
ASTContext &Ctx) {
- // FIXME: impl me!
+
+ // FIXME: I suppose finding contributor Decls is commonly needed by all/many
+ // extractors
+ class ContributorFinder : public DynamicRecursiveASTVisitor {
+ public:
+ std::vector<const NamedDecl *> Contributors;
+
+ bool VisitFunctionDecl(FunctionDecl *D) override {
+ Contributors.push_back(D);
+ return true;
+ }
+
+ bool VisitRecordDecl(RecordDecl *D) override {
+ Contributors.push_back(D);
+ return true;
+ }
+
+ bool VisitVarDecl(VarDecl *D) override {
+ DeclContext *DC = D->getDeclContext();
+
+ if (DC->isFileContext() || DC->isNamespace())
+ Contributors.push_back(D);
+ return false;
+ }
+ } ContributorFinder;
+
+ ContributorFinder.VisitTranslationUnitDecl(Ctx.getTranslationUnitDecl());
+
+ llvm::Error Errors = llvm::ErrorSuccess();
+ auto addError = [&Errors](llvm::Error Err) {
+ Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
+ };
+
+ for (auto *CD : ContributorFinder.Contributors) {
+ llvm::Error Error = llvm::ErrorSuccess();
+ auto EntitySummary = extractEntitySummary(CD, Ctx, Error);
+
+ if (Error)
+ addError(std::move(Error));
+ if (EntitySummary->empty())
+ continue;
+
+ auto ContributorName = getEntityName(CD);
+
+ if (!ContributorName) {
+ addError(makeCreateEntityNameError(CD, Ctx));
+ continue;
+ }
+
+ auto [EntitySummaryPtr, Success] = SummaryBuilder.addSummary(
+ addEntity(*ContributorName), std::move(EntitySummary));
+
+ if (!Success)
+ addError(makeAddEntitySummaryError(CD, Ctx));
+ }
+ // FIXME: handle errors!
+ llvm::consumeError(std::move(Errors));
}
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 81c742dc30811..67eec3714047b 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -35,7 +35,7 @@ const SomeDecl *findDeclByName(StringRef Name, ASTContext &Ctx) {
NamedDeclFinder(StringRef SearchingName) : SearchingName(SearchingName) {}
bool VisitDecl(Decl *D) override {
- if (const auto *ND = dyn_cast<NamedDecl>(D)) {
+ if (const auto *ND = dyn_cast<SomeDecl>(D)) {
if (ND->getNameAsString() == SearchingName) {
FoundDecl = ND;
return false;
@@ -69,16 +69,21 @@ class UnsafeBufferUsageTest : public testing::Test {
: TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
TUSummaryBuilder(TUSum), Extractor(TUSummaryBuilder) {}
+ template <typename ContributorDecl = NamedDecl>
std::unique_ptr<UnsafeBufferUsageEntitySummary>
setUpTest(StringRef Code, StringRef ContributorName) {
AST = tooling::buildASTFromCodeWithArgs(
- Code, {"-Wno-unused-value -Wno-int-to-pointer-cast"});
+ Code, {"-Wno-unused-value", "-Wno-int-to-pointer-cast"});
const auto *ContributorDefn =
- findDeclByName(ContributorName, AST->getASTContext());
+ findDeclByName<ContributorDecl>(ContributorName, AST->getASTContext());
+
+ if (!ContributorDefn)
+ return nullptr;
+
std::optional<EntityName> EN = getEntityName(ContributorDefn);
- if (!ContributorDefn || !EN)
+ if (!EN)
return nullptr;
llvm::Error Error = llvm::ErrorSuccess();
@@ -442,4 +447,53 @@ TEST_F(UnsafeBufferUsageTest, PointerToArrayOfPointers) {
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 3}}));
}
+
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInGlobalVariableInitializer) {
+ auto Sum = setUpTest(R"cpp(
+ int *gp;
+ int x = gp[5];
+ )cpp",
+ {"x"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer) {
+ auto Sum = setUpTest<>(R"cpp(
+ int *gp;
+ struct Foo {
+ int field = gp[5];
+ };
+ )cpp",
+ {"Foo"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInCXXCtorInitializer) {
+ auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
+ struct Foo {
+ int member;
+ Foo(int *p) : member(p[5]) {}
+ };
+ )cpp",
+ {"Foo"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInDefaultArg) {
+ auto Sum = setUpTest(R"cpp(
+ int * gp;
+ void foo(int x = gp[5]);
+ )cpp",
+ {"foo"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
} // namespace
>From 613955c1b475383116fc8182aa12a21d87d63ddd Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 5 Mar 2026 14:09:34 -0800
Subject: [PATCH 03/51] clean up
---
clang/lib/Analysis/UnsafeBufferUsage.cpp | 2 +-
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index d6f554246a1b4..5a9241acbee36 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -4747,4 +4747,4 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
}
applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
std::move(Tracker), Handler, EmitSuggestions);
-}
\ No newline at end of file
+}
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 67eec3714047b..db11717a86747 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -460,7 +460,7 @@ TEST_F(UnsafeBufferUsageTest, UnsafePointerInGlobalVariableInitializer) {
}
TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer) {
- auto Sum = setUpTest<>(R"cpp(
+ auto Sum = setUpTest(R"cpp(
int *gp;
struct Foo {
int field = gp[5];
>From deef1a0a65f6e03f19f6fa74ea232b6149b570be Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Mon, 16 Mar 2026 17:36:39 -0700
Subject: [PATCH 04/51] Address comments
---
.../UnsafeBufferUsageTest.cpp | 80 +++++++++++++++++++
1 file changed, 80 insertions(+)
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index db11717a86747..8108ef8ad77b5 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -472,6 +472,35 @@ TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer) {
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
}
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer2) {
+ auto Sum = setUpTest(R"cpp(
+ int *gp;
+ union Foo {
+ int field = gp[5];
+ int x;
+ };
+ )cpp",
+ {"Foo"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, InitializerList) {
+ auto Sum = setUpTest(R"cpp(
+ int *gp;
+ struct Foo {
+ int field;
+ int x;
+ };
+ Foo FooObj{gp[5], 0};
+ )cpp",
+ {"FooObj"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
TEST_F(UnsafeBufferUsageTest, UnsafePointerInCXXCtorInitializer) {
auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
struct Foo {
@@ -496,4 +525,55 @@ TEST_F(UnsafeBufferUsageTest, UnsafePointerInDefaultArg) {
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
}
+TEST_F(UnsafeBufferUsageTest, NestedDefinitions) {
+ StringRef Code = R"cpp(
+ int * a = [](){
+ struct Foo {
+ void bar(int * ptr) { ptr[3] = 0; }
+ };
+ return nullptr;
+ }();
+ )cpp";
+ auto Sum = setUpTest(Code, {"bar"});
+
+ EXPECT_NE(Sum, nullptr);
+ // The closest contributor owns the fact:
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}}));
+
+ Sum = setUpTest(Code, {"Foo"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_TRUE(Sum->empty());
+
+ Sum = setUpTest(Code, {"a"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_TRUE(Sum->empty());
+}
+
+TEST_F(UnsafeBufferUsageTest, NestedDefinitions2) {
+ StringRef Code = R"cpp(
+ int main(void) {
+ struct Foo {
+ void bar(int * ptr) { ptr[3] = 0; }
+ };
+ }
+ )cpp";
+ auto Sum = setUpTest(Code, {"bar"});
+
+ EXPECT_NE(Sum, nullptr);
+ // The closest contributor owns the fact:
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}}));
+
+ Sum = setUpTest(Code, {"Foo"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_TRUE(Sum->empty());
+
+ Sum = setUpTest(Code, {"main"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_TRUE(Sum->empty());
+}
+
} // namespace
>From 5ffde95abf78f9b14d9d3d64afaef3360559bee3 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 12 Mar 2026 14:10:19 -0700
Subject: [PATCH 05/51] Reapply "[clang][ssaf] Add UnsafeBufferUsage summary
extractor for functions (#182941)"
This reverts commit 53739c75a8720aaef8032628267ed4fd050af038.
Reapply after module dependency issues are resolved.
(rdar://169191570)
---
.../UnsafeBufferUsage/UnsafeBufferUsage.h | 56 +--
.../UnsafeBufferUsageExtractor.h | 40 ++
.../UnsafeBufferUsageBuilder.h | 32 --
.../Analyses/CMakeLists.txt | 15 +
.../UnsafeBufferUsageExtractor.cpp | 281 ++++++++++++
.../CMakeLists.txt | 2 +
.../UnsafeBufferUsageTest.cpp | 423 ++++++++++++++++--
.../CMakeLists.txt | 1 +
8 files changed, 743 insertions(+), 107 deletions(-)
rename clang/include/clang/ScalableStaticAnalysisFramework/{Core => }/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h (62%)
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
delete mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
similarity index 62%
rename from clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
rename to clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 5b58ed0684333..5f418000723b4 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -6,13 +6,12 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
+#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/Core/Model/SummaryName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
-#include "llvm/ADT/iterator_range.h"
#include <set>
namespace clang::ssaf {
@@ -26,23 +25,19 @@ namespace clang::ssaf {
/// *' 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');
-/// - 'P.EntityId' identifies an lvalue object and 'P.PointerLevel == 0'.
-/// The latter case represents address-of expressions.
+/// 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, 1)' is associated with 'int *[10]' of 'p';
-/// '(p, 2)' is associated with 'int *' of 'p';
-/// '(p, 0)' represents '&p'.
+/// - '(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 UnsafeBufferUsageTUSummaryBuilder;
- friend class UnsafeBufferUsageEntitySummary;
+ friend class UnsafeBufferUsageTUSummaryExtractor;
EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
: Entity(Entity), PointerLevel(PointerLevel) {}
@@ -52,7 +47,8 @@ class EntityPointerLevel {
unsigned getPointerLevel() const { return PointerLevel; }
bool operator==(const EntityPointerLevel &Other) const {
- return Entity == Other.Entity && PointerLevel == Other.PointerLevel;
+ return std::tie(Entity, PointerLevel) ==
+ std::tie(Other.Entity, Other.PointerLevel);
}
bool operator!=(const EntityPointerLevel &Other) const {
@@ -64,7 +60,8 @@ class EntityPointerLevel {
std::tie(Other.Entity, Other.PointerLevel);
}
- // Comparator supporting partial comparison against EntityId:
+ /// Compares `EntityPointerLevel`s; additionally, partially compares
+ /// `EntityPointerLevel` with `EntityId`.
struct Comparator {
using is_transparent = void;
bool operator()(const EntityPointerLevel &L,
@@ -88,33 +85,20 @@ using EntityPointerLevelSet =
class UnsafeBufferUsageEntitySummary final : public EntitySummary {
const EntityPointerLevelSet UnsafeBuffers;
- friend class UnsafeBufferUsageTUSummaryBuilder;
+ friend class UnsafeBufferUsageTUSummaryExtractor;
- UnsafeBufferUsageEntitySummary(EntityPointerLevelSet &&UnsafeBuffers)
+ UnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers)
: EntitySummary(), UnsafeBuffers(std::move(UnsafeBuffers)) {}
public:
- using const_iterator = EntityPointerLevelSet::const_iterator;
-
- const_iterator begin() const { return UnsafeBuffers.begin(); }
- const_iterator end() const { return UnsafeBuffers.end(); }
-
- const_iterator find(const EntityPointerLevel &V) const {
- return UnsafeBuffers.find(V);
- }
-
- llvm::iterator_range<const_iterator> getSubsetOf(EntityId Entity) const {
- return llvm::make_range(UnsafeBuffers.equal_range(Entity));
- }
-
- /// \return the size of the set of EntityLevelPointers, which represents the
- /// set of unsafe buffers
- size_t getNumUnsafeBuffers() { return UnsafeBuffers.size(); }
-
SummaryName getSummaryName() const override {
return SummaryName{"UnsafeBufferUsage"};
};
+
+ bool operator==(const EntityPointerLevelSet &Other) const {
+ return UnsafeBuffers == Other;
+ }
};
} // namespace clang::ssaf
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
new file mode 100644
index 0000000000000..765b2c37562ce
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
@@ -0,0 +1,40 @@
+//===- UnsafeBufferUsageExtractor.h -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
+
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
+#include "llvm/Support/Error.h"
+#include <memory>
+
+namespace clang::ssaf {
+class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor {
+public:
+ UnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder)
+ : TUSummaryExtractor(Builder) {}
+
+ static EntityPointerLevel buildEntityPointerLevel(EntityId Entity,
+ unsigned PointerLevel) {
+ return {Entity, PointerLevel};
+ }
+
+ EntityId addEntity(EntityName EN) { return SummaryBuilder.addEntity(EN); }
+
+ std::unique_ptr<UnsafeBufferUsageEntitySummary>
+ extractEntitySummary(const Decl *Contributor, ASTContext &Ctx,
+ llvm::Error &Error);
+
+ void HandleTranslationUnit(ASTContext &Ctx) override;
+};
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h b/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h
deleted file mode 100644
index db6c097510a57..0000000000000
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h
+++ /dev/null
@@ -1,32 +0,0 @@
-//===- UnsafeBufferUsageBuilder.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_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
-
-#include "clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
-#include <memory>
-
-namespace clang::ssaf {
-class UnsafeBufferUsageTUSummaryBuilder : public TUSummaryBuilder {
-public:
- static EntityPointerLevel buildEntityPointerLevel(EntityId Entity,
- unsigned PointerLevel) {
- return {Entity, PointerLevel};
- }
-
- static std::unique_ptr<UnsafeBufferUsageEntitySummary>
- buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet &&UnsafeBuffers) {
- return std::make_unique<UnsafeBufferUsageEntitySummary>(
- UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers)));
- }
-};
-} // namespace clang::ssaf
-
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_CORE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
new file mode 100644
index 0000000000000..34c6dd9b61203
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses
+ UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+
+ LINK_LIBS
+ clangAST
+ clangAnalysis
+ clangBasic
+ clangScalableStaticAnalysisFrameworkCore
+
+ DEPENDS
+ )
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
new file mode 100644
index 0000000000000..c150b2918db59
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -0,0 +1,281 @@
+//===- UnsafeBufferUsageExtractor.cpp -------------------------------------===//
+//
+// 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/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Error.h"
+#include <memory>
+
+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;
+
+// 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 llvm::createStringError(
+ "unsupported expression kind for translation to "
+ "EntityPointerLevel: %s",
+ E->getStmtClassName());
+ }
+
+ 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;
+
+public:
+ EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor)
+ : Extractor(Extractor) {}
+
+ 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 llvm::createStringError(
+ "failed to create entity name from the Decl of " +
+ E->getNameInfo().getAsString());
+ }
+
+ // Translate({., ->}f) -> {(MemberDecl, 1)}
+ Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
+ if (auto EntityName = getEntityName(E->getMemberDecl()))
+ return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
+ return llvm::createStringError(
+ "failed to create entity name from the MemberDecl of " +
+ E->getMemberDecl()->getNameAsString());
+ }
+
+ Expected<EntityPointerLevelSet>
+ VisitOpaqueValueExpr(const OpaqueValueExpr *S) {
+ return Visit(S->getSourceExpr());
+ }
+};
+
+Expected<EntityPointerLevelSet>
+buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
+ UnsafeBufferUsageTUSummaryExtractor &Extractor) {
+ EntityPointerLevelSet Result{};
+ EntityPointerLevelTranslator Translator{Extractor};
+ llvm::Error AllErrors = llvm::ErrorSuccess();
+
+ for (const Expr *Ptr : UnsafePointers) {
+ Expected<EntityPointerLevelSet> Translation = Translator.translate(Ptr);
+
+ if (Translation) {
+ // Filter out those temporary invalid EntityPointerLevels associated with
+ // `&E` pointers:
+ auto FilteredTranslation = llvm::make_filter_range(
+ *Translation, [](const EntityPointerLevel &E) -> bool {
+ return E.getPointerLevel() > 0;
+ });
+ Result.insert(FilteredTranslation.begin(), FilteredTranslation.end());
+ continue;
+ }
+ AllErrors = llvm::joinErrors(std::move(AllErrors), Translation.takeError());
+ }
+ if (AllErrors)
+ return AllErrors;
+ return Result;
+}
+} // namespace
+
+std::unique_ptr<UnsafeBufferUsageEntitySummary>
+UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary(
+ const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) {
+ // FIXME: findUnsafePointers should accept more kinds of `Decl`s than just
+ // `FunctionDecl`:
+ if (const auto *FD = dyn_cast<FunctionDecl>(Contributor)) {
+ Expected<EntityPointerLevelSet> EPLs =
+ buildEntityPointerLevels(findUnsafePointers(FD), *this);
+
+ if (EPLs)
+ return std::make_unique<UnsafeBufferUsageEntitySummary>(
+ UnsafeBufferUsageEntitySummary(std::move(*EPLs)));
+ Error = EPLs.takeError();
+ }
+ return nullptr;
+}
+
+void UnsafeBufferUsageTUSummaryExtractor::HandleTranslationUnit(
+ ASTContext &Ctx) {
+ // FIXME: impl me!
+}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
index d3d75430233fe..6bcb1a0038127 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -1,2 +1,4 @@
+add_subdirectory(Analyses)
add_subdirectory(Core)
add_subdirectory(Frontend)
+
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 082ff7f8ac175..81c742dc30811 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -6,37 +6,144 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
using namespace clang;
using namespace ssaf;
+using testing::UnorderedElementsAre;
namespace {
+template <typename SomeDecl = NamedDecl>
+const SomeDecl *findDeclByName(StringRef Name, ASTContext &Ctx) {
+ class NamedDeclFinder : public DynamicRecursiveASTVisitor {
+ public:
+ StringRef SearchingName;
+ const NamedDecl *FoundDecl = nullptr;
+
+ NamedDeclFinder(StringRef SearchingName) : SearchingName(SearchingName) {}
+
+ bool VisitDecl(Decl *D) override {
+ if (const auto *ND = dyn_cast<NamedDecl>(D)) {
+ if (ND->getNameAsString() == SearchingName) {
+ FoundDecl = ND;
+ return false;
+ }
+ }
+ return true;
+ }
+ };
+
+ NamedDeclFinder Finder(Name);
+
+ Finder.TraverseDecl(Ctx.getTranslationUnitDecl());
+ return dyn_cast_or_null<SomeDecl>(Finder.FoundDecl);
+}
+
+const FunctionDecl *findFnByName(StringRef Name, ASTContext &Ctx) {
+ return findDeclByName<FunctionDecl>(Name, Ctx);
+}
+
constexpr inline auto buildEntityPointerLevel =
- UnsafeBufferUsageTUSummaryBuilder::buildEntityPointerLevel;
-constexpr inline auto buildUnsafeBufferUsageEntitySummary =
- UnsafeBufferUsageTUSummaryBuilder::buildUnsafeBufferUsageEntitySummary;
+ UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
class UnsafeBufferUsageTest : public testing::Test {
protected:
- EntityIdTable Table;
+ TUSummary TUSum;
+ TUSummaryBuilder TUSummaryBuilder;
+ UnsafeBufferUsageTUSummaryExtractor Extractor;
+ std::unique_ptr<ASTUnit> AST;
+
+ UnsafeBufferUsageTest()
+ : TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
+ TUSummaryBuilder(TUSum), Extractor(TUSummaryBuilder) {}
+
+ std::unique_ptr<UnsafeBufferUsageEntitySummary>
+ setUpTest(StringRef Code, StringRef ContributorName) {
+ AST = tooling::buildASTFromCodeWithArgs(
+ Code, {"-Wno-unused-value -Wno-int-to-pointer-cast"});
+
+ const auto *ContributorDefn =
+ findDeclByName(ContributorName, AST->getASTContext());
+ std::optional<EntityName> EN = getEntityName(ContributorDefn);
+
+ if (!ContributorDefn || !EN)
+ return nullptr;
+
+ llvm::Error Error = llvm::ErrorSuccess();
+ auto Sum = Extractor.extractEntitySummary(ContributorDefn,
+ AST->getASTContext(), Error);
+
+ if (Error) {
+ llvm::consumeError(std::move(Error));
+ return nullptr;
+ }
+ return Sum;
+ }
+
+ std::optional<EntityId> getEntityId(StringRef Name) {
+ if (const auto *D = findDeclByName(Name, AST->getASTContext()))
+ if (auto EntityName = getEntityName(D))
+ return Extractor.addEntity(*EntityName);
+ return std::nullopt;
+ }
+
+ std::optional<EntityId> getEntityIdForReturn(StringRef FunName) {
+ if (const auto *D = findFnByName(FunName, AST->getASTContext()))
+ if (auto EntityName = getEntityNameForReturn(D))
+ return Extractor.addEntity(*EntityName);
+ return std::nullopt;
+ }
+
+ // Same as `std::pair<StringName, unsigned>` for a pair of entity declaration
+ // name and a pointer level with an extra optional flag for whether the entity
+ // represents a function return value:
+ struct EPLPair {
+ EPLPair(StringRef Name, unsigned Lv, bool isFunRet = false)
+ : Name(Name), Lv(Lv), isFunRet(isFunRet) {}
+
+ StringRef Name;
+ unsigned Lv;
+ bool isFunRet;
+ };
+
+ EntityPointerLevelSet makeSet(unsigned Line, ArrayRef<EPLPair> Pairs) {
+ auto EPLs = llvm::map_range(
+ Pairs, [this, Line](const EPLPair &Pair) -> EntityPointerLevel {
+ std::optional<EntityId> Entity = Pair.isFunRet
+ ? getEntityIdForReturn(Pair.Name)
+ : getEntityId(Pair.Name);
+ if (!Entity)
+ ADD_FAILURE_AT(__FILE__, Line) << "Entity not found: " << Pair.Name;
+ return buildEntityPointerLevel(*Entity, Pair.Lv);
+ });
+ return EntityPointerLevelSet{EPLs.begin(), EPLs.end()};
+ }
};
//////////////////////////////////////////////////////////////
// Data Structure Tests //
//////////////////////////////////////////////////////////////
-#define EXPECT_CONTAINS(Set, Elt) EXPECT_NE((Set).find(Elt), (Set).end())
-#define EXPECT_EXCLUDES(Set, Elt) EXPECT_EQ((Set).find(Elt), (Set).end())
+static llvm::iterator_range<EntityPointerLevelSet::iterator>
+getSubsetOf(const EntityPointerLevelSet &Set, EntityId Entity) {
+ return llvm::make_range(Set.equal_range(Entity));
+}
TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
- EntityId E1 = Table.getId({"c:@F at foo", "", {}});
- EntityId E2 = Table.getId({"c:@F at bar", "", {}});
+ EntityId E1 = Extractor.addEntity({"c:@F at foo", "", {}});
+ EntityId E2 = Extractor.addEntity({"c:@F at bar", "", {}});
auto P1 = buildEntityPointerLevel(E1, 2);
auto P2 = buildEntityPointerLevel(E1, 2);
@@ -53,48 +160,286 @@ TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
EXPECT_FALSE(P2 < P1);
}
-TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntitySummaryTest) {
- EntityId E1 = Table.getId({"c:@F at foo", "", {}});
- EntityId E2 = Table.getId({"c:@F at bar", "", {}});
- EntityId E3 = Table.getId({"c:@F at baz", "", {}});
+TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
+ EntityId E1 = Extractor.addEntity({"c:@F at foo", "", {}});
+ EntityId E2 = Extractor.addEntity({"c:@F at bar", "", {}});
+ EntityId E3 = Extractor.addEntity({"c:@F at baz", "", {}});
auto P1 = buildEntityPointerLevel(E1, 1);
auto P2 = buildEntityPointerLevel(E1, 2);
auto P3 = buildEntityPointerLevel(E2, 1);
auto P4 = buildEntityPointerLevel(E2, 2);
auto P5 = buildEntityPointerLevel(E3, 1);
- auto P6 = buildEntityPointerLevel(E3, 2);
EntityPointerLevelSet Set{P1, P2, P3, P4, P5};
- auto ES = buildUnsafeBufferUsageEntitySummary(std::move(Set));
- ASSERT_TRUE(ES);
- EXPECT_CONTAINS(*ES, P1);
- EXPECT_CONTAINS(*ES, P2);
- EXPECT_CONTAINS(*ES, P3);
- EXPECT_CONTAINS(*ES, P4);
- EXPECT_CONTAINS(*ES, P5);
- EXPECT_EXCLUDES(*ES, P6);
+ EXPECT_THAT(Set, UnorderedElementsAre(P1, P2, P3, P4, P5));
+ EXPECT_THAT(getSubsetOf(Set, E1), UnorderedElementsAre(P1, P2));
+ EXPECT_THAT(getSubsetOf(Set, E2), UnorderedElementsAre(P3, P4));
+ EXPECT_THAT(getSubsetOf(Set, E3), UnorderedElementsAre(P5));
+}
+
+//////////////////////////////////////////////////////////////
+// Extractor Tests //
+//////////////////////////////////////////////////////////////
+
+TEST_F(UnsafeBufferUsageTest, SimpleFunctionWithUnsafePointer) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p) {
+ p[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
- EntityPointerLevelSet Subset1{ES->getSubsetOf(E1).begin(),
- ES->getSubsetOf(E1).end()};
+TEST_F(UnsafeBufferUsageTest, PointerArithmetic) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q) {
+ *(p + 5);
+ *(q - 3);
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, PointerIncrementDecrement) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q, int *r, int *s) {
+ (++p)[5];
+ (q++)[5];
+ (--r)[5];
+ (s--)[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum,
+ makeSet(__LINE__, {{"p", 1U}, {"q", 1U}, {"r", 1U}, {"s", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, PointerAssignment) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q) {
+ (p = q + 5)[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, CompoundAssignment) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q) {
+ (p += 5)[5];
+ (q -= 3)[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
+}
- EXPECT_CONTAINS(Subset1, P1);
- EXPECT_CONTAINS(Subset1, P2);
- EXPECT_EQ(Subset1.size(), 2U);
+TEST_F(UnsafeBufferUsageTest, MultiLevelPointer) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int **p, int **q, int **r) {
+ (*p)[5];
+ *(*q);
+ *(q[5]);
+ r[5][5];
+ }
+ )cpp",
+ "foo");
- EntityPointerLevelSet Subset2{ES->getSubsetOf(E2).begin(),
- ES->getSubsetOf(E2).end()};
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum,
+ makeSet(__LINE__, {{"p", 2U}, {"q", 1U}, {"r", 1U}, {"r", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, ConditionalOperator) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int **p, int **q, int cond) {
+ (cond ? *p : *q)[5];
+ cond ? p[5] : q[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum,
+ makeSet(__LINE__, {{"p", 1U}, {"q", 1U}, {"p", 2U}, {"q", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, CastExpression) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(void *p, int q) {
+ ((int*)p)[5];
+ ((int*)q)[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, CommaOperator) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int x) {
+ (x++, p)[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, CommaOperator2) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int **p, int **q, int x) {
+ (p[x] = 0, q[x] = 0)[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}, {"q", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, ParenthesizedExpression) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p) {
+ (((p)))[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, ArrayParameter) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int arr[], int arr2[][10]) {
+ int n = 5;
+ arr[100];
+ arr2[5][n];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"arr", 1U}, {"arr2", 1U}, {"arr2", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, FunctionCall) {
+ auto Sum = setUpTest(R"cpp(
+ int ** (*fp)();
+ int ** foo() {
+ fp = &foo;
+ foo()[5];
+ (*fp())[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ // No (foo, 2) becasue indirect calls are ignored.
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"foo", 1U, true}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, StructMemberAccess) {
+ auto Sum = setUpTest(R"cpp(
+ struct S {
+ int *ptr;
+ int (*ptr_to_arr)[10];
+ };
+ void foo(struct S obj) {
+ int n = 5;
+ obj.ptr[5];
+ (*obj.ptr_to_arr)[n];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}, {"ptr_to_arr", 2U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, StringLiteralSubscript) {
+ auto Sum = setUpTest(R"cpp(
+ void foo() {
+ "hello"[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ // String literals should not generate pointer kind variables
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {}));
+}
+
+TEST_F(UnsafeBufferUsageTest, OpaqueValueExpr) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q) {
+ (p ?: q)[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, AddressOfOperator) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int x) {
+ (&x)[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ // Address-of should not generate pointer kind variables for 'x'
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {}));
+}
+
+TEST_F(UnsafeBufferUsageTest, AddressOfThenDereference) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q) {
+ (*(&p))[5];
+ (&(*q))[5];
+ }
+ )cpp",
+ "foo");
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1}, {"q", 1}}));
+}
- EXPECT_CONTAINS(Subset2, P3);
- EXPECT_CONTAINS(Subset2, P4);
- EXPECT_EQ(Subset2.size(), 2U);
+TEST_F(UnsafeBufferUsageTest, PointerToArrayOfPointers) {
+ auto Sum = setUpTest(R"cpp(
+ void foo() {
+ int * arr[10];
+ int * (*p)[10] = arr;
- EntityPointerLevelSet Subset3{ES->getSubsetOf(E3).begin(),
- ES->getSubsetOf(E3).end()};
+ (*p)[5][5]; // '(*p)[5]' is unsafe
+ // '(*p)' is fine because 5 < 10
+ }
+ )cpp",
+ "foo");
- EXPECT_CONTAINS(Subset3, P5);
- EXPECT_EXCLUDES(Subset3, P6);
- EXPECT_EQ(Subset3.size(), 1U);
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 3}}));
}
} // namespace
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
index 871d9e6b0c02c..f541e81d42789 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -29,6 +29,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
clangASTMatchers
clangBasic
clangFrontend
+ clangScalableStaticAnalysisFrameworkAnalyses
clangScalableStaticAnalysisFrameworkCore
clangScalableStaticAnalysisFrameworkFrontend
clangSerialization
>From fe7dd289875091a8f41966d9500b5c1d0e9f93a9 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 5 Mar 2026 13:00:44 -0800
Subject: [PATCH 06/51] [ssaf][UnsafeBufferUsage] Add support for extracting
unsafe pointers from all kinds of contributors
- Generalize the -Wunsafe-buffer-usage API for finding unsafe pointers in all kinds of Decls
- Add support in SSAF-based UnsafeBufferUsage analysis for extracting from various contributors
- Mock implementation of HandleTranslationUnit
rdar://171735836
---
.../Analysis/Analyses/UnsafeBufferUsage.h | 9 +-
.../UnsafeBufferUsage/UnsafeBufferUsage.h | 2 +
clang/lib/Analysis/UnsafeBufferUsage.cpp | 61 +++++---
.../UnsafeBufferUsageExtractor.cpp | 140 ++++++++++++++----
.../UnsafeBufferUsageTest.cpp | 62 +++++++-
5 files changed, 226 insertions(+), 48 deletions(-)
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index 876682ad779d4..e0d583c735e61 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -201,7 +201,14 @@ bool anyConflict(const llvm::SmallVectorImpl<FixItHint> &FixIts,
const SourceManager &SM);
} // namespace internal
-std::set<const Expr *> findUnsafePointers(const FunctionDecl *FD);
+/// Find unsafe pointers in body/initializer of `D`, if `D` is one of the
+/// followings:
+/// VarDecl
+/// FieldDecl
+/// FunctionDecl
+/// BlockDecl
+/// ObjCMethodDecl
+std::set<const Expr *> findUnsafePointers(const Decl *D);
} // end namespace clang
#endif /* LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H */
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 5f418000723b4..c0a4c2f76ab48 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -98,6 +98,8 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
bool operator==(const EntityPointerLevelSet &Other) const {
return UnsafeBuffers == Other;
}
+
+ bool empty() const { return UnsafeBuffers.empty(); }
};
} // namespace clang::ssaf
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 133e39b8fac2b..d6f554246a1b4 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -28,6 +28,7 @@
#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
@@ -36,6 +37,7 @@
#include <queue>
#include <set>
#include <sstream>
+#include <vector>
using namespace clang;
@@ -2942,7 +2944,37 @@ template <typename NodeTy> struct CompareNode {
}
};
-std::set<const Expr *> clang::findUnsafePointers(const FunctionDecl *FD) {
+// Populate `Stmts` with the body/initializer Stmt of `D`, if `D` is one of the
+// followings:
+// VarDecl
+// FieldDecl
+// FunctionDecl
+// BlockDecl
+// ObjCMethodDecl
+static void populateStmtsForFindingGadgets(SmallVector<const Stmt *> &Stmts,
+ const Decl *D) {
+ auto AddStmt = [&Stmts](const Stmt *S) {
+ if (S)
+ Stmts.push_back(S);
+ };
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ AddStmt(FD->getBody());
+ for (const auto *PD : FD->parameters())
+ AddStmt(PD->getDefaultArg());
+ if (const auto *CtorD = dyn_cast<CXXConstructorDecl>(FD))
+ llvm::append_range(
+ Stmts, llvm::map_range(CtorD->inits(),
+ std::mem_fn(&CXXCtorInitializer::getInit)));
+ } else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
+ AddStmt(D->getBody());
+ } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
+ AddStmt(VD->getInit()); // FIXME: default arg for ParmVarDecl?
+ } else if (const auto *FD = dyn_cast<FieldDecl>(D)) {
+ AddStmt(FD->getInClassInitializer());
+ }
+}
+
+std::set<const Expr *> clang::findUnsafePointers(const Decl *D) {
class MockReporter : public UnsafeBufferUsageHandler {
public:
MockReporter() {}
@@ -2981,9 +3013,13 @@ std::set<const Expr *> clang::findUnsafePointers(const FunctionDecl *FD) {
WarningGadgetList WarningGadgets;
DeclUseTracker Tracker;
MockReporter IgnoreHandler;
+ ASTContext &Ctx = D->getASTContext();
+ SmallVector<const Stmt *> Stmts;
- findGadgets(FD->getBody(), FD->getASTContext(), IgnoreHandler, false,
- FixableGadgets, WarningGadgets, Tracker);
+ populateStmtsForFindingGadgets(Stmts, D);
+ for (auto *Stmt : Stmts)
+ findGadgets(Stmt, Ctx, IgnoreHandler, false, FixableGadgets, WarningGadgets,
+ Tracker);
std::set<const Expr *> Result;
for (auto &G : WarningGadgets) {
@@ -4673,9 +4709,6 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
#endif
assert(D);
-
- SmallVector<Stmt *> Stmts;
-
if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
// Consteval functions are free of UB by the spec, so we don't need to
// visit them or produce diagnostics.
@@ -4697,27 +4730,21 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
break;
}
}
+ }
- Stmts.push_back(FD->getBody());
+ SmallVector<const Stmt *> Stmts;
- if (const auto *ID = dyn_cast<CXXConstructorDecl>(D)) {
- for (const CXXCtorInitializer *CI : ID->inits()) {
- Stmts.push_back(CI->getInit());
- }
- }
- } else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
- Stmts.push_back(D->getBody());
- }
+ populateStmtsForFindingGadgets(Stmts, D);
assert(!Stmts.empty());
FixableGadgetList FixableGadgets;
WarningGadgetList WarningGadgets;
DeclUseTracker Tracker;
- for (Stmt *S : Stmts) {
+ for (const Stmt *S : Stmts) {
findGadgets(S, D->getASTContext(), Handler, EmitSuggestions, FixableGadgets,
WarningGadgets, Tracker);
}
applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
std::move(Tracker), Handler, EmitSuggestions);
-}
+}
\ No newline at end of file
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index c150b2918db59..c609168e4dc7d 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -33,6 +33,32 @@ static bool hasPointerType(const Expr *E) {
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(
+ Ctx.getSourceManager());
+ return llvm::createStringError(
+ "failed to create entity name for %s declared at %s",
+ FailedDecl->getNameAsString().c_str(), LocStr.c_str());
+}
+
+static llvm::Error makeAddEntitySummaryError(const NamedDecl *FailedContributor,
+ ASTContext &Ctx) {
+ std::string LocStr =
+ FailedContributor->getSourceRange().getBegin().printToString(
+ Ctx.getSourceManager());
+ return llvm::createStringError(
+ "failed to add entity summary for contributor %s declared at %s",
+ FailedContributor->getNameAsString().c_str(), LocStr.c_str());
+}
+
// 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
@@ -59,10 +85,7 @@ class EntityPointerLevelTranslator
// Fallback method for all unsupported expression kind:
llvm::Error fallback(const Stmt *E) {
- return llvm::createStringError(
- "unsupported expression kind for translation to "
- "EntityPointerLevel: %s",
- E->getStmtClassName());
+ return makeUnsupportedStmtKindError(E);
}
static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
@@ -92,10 +115,12 @@ class EntityPointerLevelTranslator
}
UnsafeBufferUsageTUSummaryExtractor &Extractor;
+ ASTContext &Ctx;
public:
- EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor)
- : Extractor(Extractor) {}
+ EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor,
+ ASTContext &Ctx)
+ : Extractor(Extractor), Ctx(Ctx) {}
Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
@@ -210,18 +235,14 @@ class EntityPointerLevelTranslator
Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
if (auto EntityName = getEntityName(E->getDecl()))
return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
- return llvm::createStringError(
- "failed to create entity name from the Decl of " +
- E->getNameInfo().getAsString());
+ 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 llvm::createStringError(
- "failed to create entity name from the MemberDecl of " +
- E->getMemberDecl()->getNameAsString());
+ return makeCreateEntityNameError(E->getMemberDecl(), Ctx);
}
Expected<EntityPointerLevelSet>
@@ -232,9 +253,10 @@ class EntityPointerLevelTranslator
Expected<EntityPointerLevelSet>
buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
- UnsafeBufferUsageTUSummaryExtractor &Extractor) {
+ UnsafeBufferUsageTUSummaryExtractor &Extractor,
+ ASTContext &Ctx) {
EntityPointerLevelSet Result{};
- EntityPointerLevelTranslator Translator{Extractor};
+ EntityPointerLevelTranslator Translator{Extractor, Ctx};
llvm::Error AllErrors = llvm::ErrorSuccess();
for (const Expr *Ptr : UnsafePointers) {
@@ -258,24 +280,90 @@ buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
}
} // namespace
+static std::set<const Expr *> findUnsafePointersInContributor(const Decl *D) {
+ if (isa<FunctionDecl>(D) || isa<VarDecl>(D))
+ return findUnsafePointers(D);
+ if (auto *RD = dyn_cast<RecordDecl>(D)) {
+ std::set<const Expr *> Result;
+
+ for (const FieldDecl *FD : RD->fields()) {
+ Result.merge(findUnsafePointers(FD));
+ }
+ return Result;
+ }
+ return {};
+}
+
std::unique_ptr<UnsafeBufferUsageEntitySummary>
UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary(
const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) {
- // FIXME: findUnsafePointers should accept more kinds of `Decl`s than just
- // `FunctionDecl`:
- if (const auto *FD = dyn_cast<FunctionDecl>(Contributor)) {
- Expected<EntityPointerLevelSet> EPLs =
- buildEntityPointerLevels(findUnsafePointers(FD), *this);
-
- if (EPLs)
- return std::make_unique<UnsafeBufferUsageEntitySummary>(
- UnsafeBufferUsageEntitySummary(std::move(*EPLs)));
- Error = EPLs.takeError();
- }
+ Expected<EntityPointerLevelSet> EPLs = buildEntityPointerLevels(
+ findUnsafePointersInContributor(Contributor), *this, Ctx);
+
+ if (EPLs)
+ return std::make_unique<UnsafeBufferUsageEntitySummary>(
+ UnsafeBufferUsageEntitySummary(std::move(*EPLs)));
+ Error = EPLs.takeError();
return nullptr;
}
void UnsafeBufferUsageTUSummaryExtractor::HandleTranslationUnit(
ASTContext &Ctx) {
- // FIXME: impl me!
+
+ // FIXME: I suppose finding contributor Decls is commonly needed by all/many
+ // extractors
+ class ContributorFinder : public DynamicRecursiveASTVisitor {
+ public:
+ std::vector<const NamedDecl *> Contributors;
+
+ bool VisitFunctionDecl(FunctionDecl *D) override {
+ Contributors.push_back(D);
+ return true;
+ }
+
+ bool VisitRecordDecl(RecordDecl *D) override {
+ Contributors.push_back(D);
+ return true;
+ }
+
+ bool VisitVarDecl(VarDecl *D) override {
+ DeclContext *DC = D->getDeclContext();
+
+ if (DC->isFileContext() || DC->isNamespace())
+ Contributors.push_back(D);
+ return false;
+ }
+ } ContributorFinder;
+
+ ContributorFinder.VisitTranslationUnitDecl(Ctx.getTranslationUnitDecl());
+
+ llvm::Error Errors = llvm::ErrorSuccess();
+ auto addError = [&Errors](llvm::Error Err) {
+ Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
+ };
+
+ for (auto *CD : ContributorFinder.Contributors) {
+ llvm::Error Error = llvm::ErrorSuccess();
+ auto EntitySummary = extractEntitySummary(CD, Ctx, Error);
+
+ if (Error)
+ addError(std::move(Error));
+ if (EntitySummary->empty())
+ continue;
+
+ auto ContributorName = getEntityName(CD);
+
+ if (!ContributorName) {
+ addError(makeCreateEntityNameError(CD, Ctx));
+ continue;
+ }
+
+ auto [EntitySummaryPtr, Success] = SummaryBuilder.addSummary(
+ addEntity(*ContributorName), std::move(EntitySummary));
+
+ if (!Success)
+ addError(makeAddEntitySummaryError(CD, Ctx));
+ }
+ // FIXME: handle errors!
+ llvm::consumeError(std::move(Errors));
}
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 81c742dc30811..67eec3714047b 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -35,7 +35,7 @@ const SomeDecl *findDeclByName(StringRef Name, ASTContext &Ctx) {
NamedDeclFinder(StringRef SearchingName) : SearchingName(SearchingName) {}
bool VisitDecl(Decl *D) override {
- if (const auto *ND = dyn_cast<NamedDecl>(D)) {
+ if (const auto *ND = dyn_cast<SomeDecl>(D)) {
if (ND->getNameAsString() == SearchingName) {
FoundDecl = ND;
return false;
@@ -69,16 +69,21 @@ class UnsafeBufferUsageTest : public testing::Test {
: TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
TUSummaryBuilder(TUSum), Extractor(TUSummaryBuilder) {}
+ template <typename ContributorDecl = NamedDecl>
std::unique_ptr<UnsafeBufferUsageEntitySummary>
setUpTest(StringRef Code, StringRef ContributorName) {
AST = tooling::buildASTFromCodeWithArgs(
- Code, {"-Wno-unused-value -Wno-int-to-pointer-cast"});
+ Code, {"-Wno-unused-value", "-Wno-int-to-pointer-cast"});
const auto *ContributorDefn =
- findDeclByName(ContributorName, AST->getASTContext());
+ findDeclByName<ContributorDecl>(ContributorName, AST->getASTContext());
+
+ if (!ContributorDefn)
+ return nullptr;
+
std::optional<EntityName> EN = getEntityName(ContributorDefn);
- if (!ContributorDefn || !EN)
+ if (!EN)
return nullptr;
llvm::Error Error = llvm::ErrorSuccess();
@@ -442,4 +447,53 @@ TEST_F(UnsafeBufferUsageTest, PointerToArrayOfPointers) {
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 3}}));
}
+
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInGlobalVariableInitializer) {
+ auto Sum = setUpTest(R"cpp(
+ int *gp;
+ int x = gp[5];
+ )cpp",
+ {"x"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer) {
+ auto Sum = setUpTest<>(R"cpp(
+ int *gp;
+ struct Foo {
+ int field = gp[5];
+ };
+ )cpp",
+ {"Foo"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInCXXCtorInitializer) {
+ auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
+ struct Foo {
+ int member;
+ Foo(int *p) : member(p[5]) {}
+ };
+ )cpp",
+ {"Foo"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInDefaultArg) {
+ auto Sum = setUpTest(R"cpp(
+ int * gp;
+ void foo(int x = gp[5]);
+ )cpp",
+ {"foo"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
} // namespace
>From ebd6232da44692340e7f8049b257fb9145f006e3 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 5 Mar 2026 14:09:34 -0800
Subject: [PATCH 07/51] clean up
---
clang/lib/Analysis/UnsafeBufferUsage.cpp | 2 +-
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index d6f554246a1b4..5a9241acbee36 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -4747,4 +4747,4 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
}
applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
std::move(Tracker), Handler, EmitSuggestions);
-}
\ No newline at end of file
+}
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 67eec3714047b..db11717a86747 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -460,7 +460,7 @@ TEST_F(UnsafeBufferUsageTest, UnsafePointerInGlobalVariableInitializer) {
}
TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer) {
- auto Sum = setUpTest<>(R"cpp(
+ auto Sum = setUpTest(R"cpp(
int *gp;
struct Foo {
int field = gp[5];
>From e26fd174ac3e4cca50a8cf2c9bb446854093d12d Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Mon, 16 Mar 2026 17:36:39 -0700
Subject: [PATCH 08/51] Address comments
---
.../UnsafeBufferUsageTest.cpp | 80 +++++++++++++++++++
1 file changed, 80 insertions(+)
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index db11717a86747..8108ef8ad77b5 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -472,6 +472,35 @@ TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer) {
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
}
+TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer2) {
+ auto Sum = setUpTest(R"cpp(
+ int *gp;
+ union Foo {
+ int field = gp[5];
+ int x;
+ };
+ )cpp",
+ {"Foo"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
+TEST_F(UnsafeBufferUsageTest, InitializerList) {
+ auto Sum = setUpTest(R"cpp(
+ int *gp;
+ struct Foo {
+ int field;
+ int x;
+ };
+ Foo FooObj{gp[5], 0};
+ )cpp",
+ {"FooObj"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
+}
+
TEST_F(UnsafeBufferUsageTest, UnsafePointerInCXXCtorInitializer) {
auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
struct Foo {
@@ -496,4 +525,55 @@ TEST_F(UnsafeBufferUsageTest, UnsafePointerInDefaultArg) {
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
}
+TEST_F(UnsafeBufferUsageTest, NestedDefinitions) {
+ StringRef Code = R"cpp(
+ int * a = [](){
+ struct Foo {
+ void bar(int * ptr) { ptr[3] = 0; }
+ };
+ return nullptr;
+ }();
+ )cpp";
+ auto Sum = setUpTest(Code, {"bar"});
+
+ EXPECT_NE(Sum, nullptr);
+ // The closest contributor owns the fact:
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}}));
+
+ Sum = setUpTest(Code, {"Foo"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_TRUE(Sum->empty());
+
+ Sum = setUpTest(Code, {"a"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_TRUE(Sum->empty());
+}
+
+TEST_F(UnsafeBufferUsageTest, NestedDefinitions2) {
+ StringRef Code = R"cpp(
+ int main(void) {
+ struct Foo {
+ void bar(int * ptr) { ptr[3] = 0; }
+ };
+ }
+ )cpp";
+ auto Sum = setUpTest(Code, {"bar"});
+
+ EXPECT_NE(Sum, nullptr);
+ // The closest contributor owns the fact:
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}}));
+
+ Sum = setUpTest(Code, {"Foo"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_TRUE(Sum->empty());
+
+ Sum = setUpTest(Code, {"main"});
+
+ EXPECT_NE(Sum, nullptr);
+ EXPECT_TRUE(Sum->empty());
+}
+
} // namespace
>From 9a212055d2a271f8bdb3ef254d73c360ca9a539f Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Tue, 10 Mar 2026 12:05:51 -0700
Subject: [PATCH 09/51] [ssaf][UnsafeBufferUsage] Add JSON serialization for
UnsafeBufferUsage
Implemented and registered a JSONFormat::FormatInfo for
UnsafeBufferUsage analysis
rdar://171920065
---
.../UnsafeBufferUsage/UnsafeBufferUsage.h | 28 +++-
.../SSAFBuiltinForceLinker.h | 4 +
.../Analyses/CMakeLists.txt | 3 +-
.../UnsafeBufferUsage/UnsafeBufferUsage.cpp | 89 ++++++++++++
.../Frontend/CMakeLists.txt | 1 +
.../tu-summary-serialization.test | 4 +
.../UnsafeBufferUsage/tu-summary.json | 108 +++++++++++++++
.../Analysis/Scalable/ssaf-format/list.test | 3 +-
clang/tools/clang-ssaf-format/CMakeLists.txt | 3 +-
clang/tools/clang-ssaf-linker/CMakeLists.txt | 1 +
.../UnsafeBufferUsageTest.cpp | 130 +++++++++++++++++-
.../CMakeLists.txt | 1 -
12 files changed, 363 insertions(+), 12 deletions(-)
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index c0a4c2f76ab48..2b36c47fe67a5 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -11,6 +11,7 @@
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
#include <set>
@@ -38,6 +39,7 @@ class EntityPointerLevel {
unsigned PointerLevel;
friend class UnsafeBufferUsageTUSummaryExtractor;
+ friend class UnsafeBufferUsageEntitySummary;
EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
: Entity(Entity), PointerLevel(PointerLevel) {}
@@ -91,15 +93,35 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
: EntitySummary(), UnsafeBuffers(std::move(UnsafeBuffers)) {}
public:
- SummaryName getSummaryName() const override {
- return SummaryName{"UnsafeBufferUsage"};
- };
+ SummaryName getSummaryName() const override { return summaryName(); };
bool operator==(const EntityPointerLevelSet &Other) const {
return UnsafeBuffers == Other;
}
+ bool operator==(const UnsafeBufferUsageEntitySummary &Other) const {
+ return UnsafeBuffers == Other.UnsafeBuffers;
+ }
+
bool empty() const { return UnsafeBuffers.empty(); }
+
+ static llvm::json::Object
+ jsonSerializeFn(const EntitySummary &ES,
+ JSONFormat::EntityIdToJSONFn EntityId2JSON);
+
+ static llvm::Expected<std::unique_ptr<EntitySummary>>
+ jsonDeserializeFn(const llvm::json::Object &Data, EntityIdTable &,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
+
+ static SummaryName summaryName() { return SummaryName{"UnsafeBufferUsage"}; }
+};
+
+struct UnsafeBufferUsageJSONFormatInfo : JSONFormat::FormatInfo {
+ UnsafeBufferUsageJSONFormatInfo()
+ : JSONFormat::FormatInfo(
+ UnsafeBufferUsageEntitySummary::summaryName(),
+ UnsafeBufferUsageEntitySummary::jsonSerializeFn,
+ UnsafeBufferUsageEntitySummary::jsonDeserializeFn) {}
};
} // namespace clang::ssaf
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
index 5f201487ca1fe..e0a394da1a921 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
@@ -25,4 +25,8 @@ extern volatile int SSAFJSONFormatAnchorSource;
[[maybe_unused]] static int SSAFJSONFormatAnchorDestination =
SSAFJSONFormatAnchorSource;
+extern volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource;
+[[maybe_unused]] static int UnsafeBufferUsageSSAFJSONFormatAnchorDestination =
+ UnsafeBufferUsageSSAFJSONFormatAnchorSource;
+
#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINFORCELINKER_H
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
index 34c6dd9b61203..c85fa044c1e9f 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -3,7 +3,8 @@ set(LLVM_LINK_COMPONENTS
)
add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses
- UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+ UnsafeBufferUsage/UnsafeBufferUsage.cpp
+ UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
LINK_LIBS
clangAST
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
new file mode 100644
index 0000000000000..44eaa20c74236
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -0,0 +1,89 @@
+//===---------- UnsafeBufferUsage.cpp -------------------------------------===//
+//
+// 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/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
+
+namespace {
+constexpr const char *const UnsafeBuffersKey = "UnsafeBuffers";
+} // namespace
+
+namespace clang::ssaf {
+using Object = llvm::json::Object;
+using Array = llvm::json::Array;
+using Value = llvm::json::Value;
+
+llvm::json::Object UnsafeBufferUsageEntitySummary::jsonSerializeFn(
+ const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+ // Writes a EntityPointerLevel as
+ // Array {
+ // Object {
+ // "@" : [entity-id]
+ // },
+ // [pointer-level]
+ // }
+ Array UnsafeBuffersData;
+
+ for (const auto &EPL :
+ static_cast<const UnsafeBufferUsageEntitySummary &>(ES).UnsafeBuffers)
+ UnsafeBuffersData.push_back(
+ Value(Array{// EntityId:
+ Value(EntityId2JSON(EPL.getEntity())),
+ // PointerLevel:
+ Value(EPL.getPointerLevel())}));
+
+ Object Data;
+
+ Data[UnsafeBuffersKey] = Value(std::move(UnsafeBuffersData));
+ return Data;
+}
+
+llvm::Expected<std::unique_ptr<EntitySummary>>
+UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
+ const llvm::json::Object &Data, EntityIdTable &,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
+ const Array *UnsafeBuffersData = Data.getArray(UnsafeBuffersKey);
+ constexpr const char *const ErrMsg = "unrecognized UnsafeBufferUsageEntitySummary data";
+
+ if (!UnsafeBuffersData)
+ return llvm::createStringError(ErrMsg);
+
+ EntityPointerLevelSet UnsafeBuffers;
+
+ for (auto &EltData : *UnsafeBuffersData) {
+ const Array *EltDataAsArr = EltData.getAsArray();
+
+ if (!EltDataAsArr || EltDataAsArr->size() != 2)
+ return llvm::createStringError(ErrMsg);
+
+ const Object *IdData = (*EltDataAsArr)[0].getAsObject();
+ std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
+
+ if (!IdData || !PtrLvData)
+ return llvm::createStringError(ErrMsg);
+
+ llvm::Expected<EntityId> Id = EntityIdFromJSON(*IdData);
+
+ if (!Id)
+ return Id.takeError();
+ UnsafeBuffers.insert(EntityPointerLevel(Id.get(), *PtrLvData));
+ }
+ return std::make_unique<UnsafeBufferUsageEntitySummary>(
+ UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers)));
+}
+
+static llvm::Registry<JSONFormat::FormatInfo>::Add<
+ UnsafeBufferUsageJSONFormatInfo>
+ RegisterUnsafeBufferUsageJSONFormatInfo(
+ "UnsafeBufferUsage",
+ "JSON Format info for UnsafeBufferUsageEntitySummary");
+
+} // namespace clang::ssaf
+
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource = 0;
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Frontend/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Frontend/CMakeLists.txt
index b90d9c0ded1a9..3da1558810572 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Frontend/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Frontend/CMakeLists.txt
@@ -9,6 +9,7 @@ add_clang_library(clangScalableStaticAnalysisFrameworkFrontend
clangAST
clangBasic
clangFrontend
+ clangScalableStaticAnalysisFrameworkAnalyses
clangScalableStaticAnalysisFrameworkCore
clangSema
)
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
new file mode 100644
index 0000000000000..e8cf6020df16f
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
@@ -0,0 +1,4 @@
+// RUN: rm -f %t.json
+// RUN: clang-ssaf-format -type=tu %S/tu-summary.json -o %t.json
+// RUN: diff %S/tu-summary.json %t.json
+//
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json
new file mode 100644
index 0000000000000..382b294ccc7bd
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json
@@ -0,0 +1,108 @@
+{
+ "data": [
+ {
+ "summary_data": [
+ {
+ "entity_id": 2,
+ "entity_summary": {
+ "UnsafeBuffers": [
+ [
+ {
+ "@": 0
+ },
+ 1
+ ],
+ [
+ {
+ "@": 0
+ },
+ 2
+ ],
+ [
+ {
+ "@": 0
+ },
+ 3
+ ],
+ [
+ {
+ "@": 1
+ },
+ 1
+ ],
+ [
+ {
+ "@": 1
+ },
+ 2
+ ],
+ [
+ {
+ "@": 1
+ },
+ 3
+ ],
+ [
+ {
+ "@": 1
+ },
+ 4
+ ]
+ ]
+ }
+ }
+ ],
+ "summary_name": "UnsafeBufferUsage"
+ }
+ ],
+ "id_table": [
+ {
+ "id": 2,
+ "name": {
+ "namespace": [],
+ "suffix": "",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ },
+ {
+ "id": 0,
+ "name": {
+ "namespace": [],
+ "suffix": "1",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ },
+ {
+ "id": 1,
+ "name": {
+ "namespace": [],
+ "suffix": "2",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ },
+ {
+ "id": 1,
+ "linkage": {
+ "type": "Internal"
+ }
+ },
+ {
+ "id": 2,
+ "linkage": {
+ "type": "Internal"
+ }
+ }
+ ],
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "Mock.cpp"
+ }
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/list.test b/clang/test/Analysis/Scalable/ssaf-format/list.test
index 4d389d78543ef..c965f7dd15f23 100644
--- a/clang/test/Analysis/Scalable/ssaf-format/list.test
+++ b/clang/test/Analysis/Scalable/ssaf-format/list.test
@@ -6,4 +6,5 @@
// CHECK: Registered serialization formats:
// CHECK-EMPTY:
// CHECK-NEXT: 1. json JSON serialization format
-// CHECK-NEXT: Analyses: (none)
+// CHECK-NEXT: Analyses:
+// CHECK-NEXT: 1.1. UnsafeBufferUsage JSON Format info for UnsafeBufferUsageEntitySummary
diff --git a/clang/tools/clang-ssaf-format/CMakeLists.txt b/clang/tools/clang-ssaf-format/CMakeLists.txt
index f64d12eb3fac0..08d091d93bd77 100644
--- a/clang/tools/clang-ssaf-format/CMakeLists.txt
+++ b/clang/tools/clang-ssaf-format/CMakeLists.txt
@@ -10,5 +10,6 @@ add_clang_tool(clang-ssaf-format
clang_target_link_libraries(clang-ssaf-format
PRIVATE
clangBasic
- clangScalableStaticAnalysisFrameworkCore
+ clangScalableStaticAnalysisFrameworkAnalyses
+ clangScalableStaticAnalysisFrameworkCore
)
diff --git a/clang/tools/clang-ssaf-linker/CMakeLists.txt b/clang/tools/clang-ssaf-linker/CMakeLists.txt
index 0f6041fb109c9..d1880843ef7fd 100644
--- a/clang/tools/clang-ssaf-linker/CMakeLists.txt
+++ b/clang/tools/clang-ssaf-linker/CMakeLists.txt
@@ -10,5 +10,6 @@ add_clang_tool(clang-ssaf-linker
clang_target_link_libraries(clang-ssaf-linker
PRIVATE
clangBasic
+ clangScalableStaticAnalysisFrameworkAnalyses
clangScalableStaticAnalysisFrameworkCore
)
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 8108ef8ad77b5..9ce39e80031fb 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -13,9 +13,11 @@
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
#include "clang/Tooling/Tooling.h"
+#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@@ -81,11 +83,6 @@ class UnsafeBufferUsageTest : public testing::Test {
if (!ContributorDefn)
return nullptr;
- std::optional<EntityName> EN = getEntityName(ContributorDefn);
-
- if (!EN)
- return nullptr;
-
llvm::Error Error = llvm::ErrorSuccess();
auto Sum = Extractor.extractEntitySummary(ContributorDefn,
AST->getASTContext(), Error);
@@ -184,6 +181,129 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
EXPECT_THAT(getSubsetOf(Set, E3), UnorderedElementsAre(P5));
}
+//////////////////////////////////////////////////////////////
+// JSON Tests //
+//////////////////////////////////////////////////////////////
+// Oracle JSON output for the example:
+// void foo(int ***p, int ****q, int x) {
+// p[5][5][5];
+// q[5][5][5][5];
+// }
+constexpr const char *const SerilizationTestOracle = R"cpp({
+ "UnsafeBuffers": [
+ [
+ {
+ "@": 42
+ },
+ 1
+ ],
+ [
+ {
+ "@": 42
+ },
+ 2
+ ],
+ [
+ {
+ "@": 42
+ },
+ 3
+ ],
+ [
+ {
+ "@": 108
+ },
+ 1
+ ],
+ [
+ {
+ "@": 108
+ },
+ 2
+ ],
+ [
+ {
+ "@": 108
+ },
+ 3
+ ],
+ [
+ {
+ "@": 108
+ },
+ 4
+ ]
+ ]
+})cpp";
+
+TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int ***p, int ****q, int x) {
+ p[5][5][5];
+ q[5][5][5][5];
+ }
+ )cpp",
+ "foo");
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U},
+ {"p", 2U},
+ {"p", 3U},
+ {"q", 1U},
+ {"q", 2U},
+ {"q", 3U},
+ {"q", 4U}}));
+
+ using Object = llvm::json::Object;
+ using Value = llvm::json::Value;
+ std::map<EntityId, uint64_t> DummyTable{{*getEntityId("p"), 42},
+ {*getEntityId("q"), 108}};
+ Object JData = UnsafeBufferUsageEntitySummary::jsonSerializeFn(
+ *Sum, [&DummyTable](EntityId Id) {
+ return Object{{"@", Value(DummyTable[Id])}};
+ });
+
+ EXPECT_EQ(llvm::formatv("{0:2}", llvm::json::Value(std::move(JData))).str(),
+ SerilizationTestOracle);
+}
+
+TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageDeserializeTest) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int ***p, int ****q, int x) {
+ p[5][5][5];
+ q[5][5][5][5];
+ }
+ )cpp",
+ "foo");
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U},
+ {"p", 2U},
+ {"p", 3U},
+ {"q", 1U},
+ {"q", 2U},
+ {"q", 3U},
+ {"q", 4U}}));
+
+ using Object = llvm::json::Object;
+ using Value = llvm::json::Value;
+ std::map<uint64_t, EntityId> DummyTable{{42, *getEntityId("p")},
+ {108, *getEntityId("q")}};
+ Expected<Value> ParsedJSON = llvm::json::parse(SerilizationTestOracle);
+
+ ASSERT_THAT_EXPECTED(ParsedJSON, llvm::Succeeded());
+ ASSERT_NE(ParsedJSON->getAsObject(), nullptr);
+
+ EntityIdTable Ignored;
+ auto ParsedSum = UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
+ *ParsedJSON->getAsObject(), Ignored,
+ [&DummyTable](const Object &O) -> Expected<EntityId> {
+ return DummyTable.at(O.getInteger("@").value());
+ });
+
+ ASSERT_THAT_EXPECTED(ParsedSum, llvm::Succeeded());
+ EXPECT_EQ(*static_cast<UnsafeBufferUsageEntitySummary *>(ParsedSum->get()),
+ *Sum);
+}
+
//////////////////////////////////////////////////////////////
// Extractor Tests //
//////////////////////////////////////////////////////////////
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
index f541e81d42789..871d9e6b0c02c 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -29,7 +29,6 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
clangASTMatchers
clangBasic
clangFrontend
- clangScalableStaticAnalysisFrameworkAnalyses
clangScalableStaticAnalysisFrameworkCore
clangScalableStaticAnalysisFrameworkFrontend
clangSerialization
>From 8e063bd570d4204f503940b9459b2349f89b112b Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Tue, 17 Mar 2026 16:10:44 -0700
Subject: [PATCH 10/51] fix a conflict merge issue
---
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 2e8b820ec0861..3555f5196a7b9 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -67,7 +67,7 @@ class UnsafeBufferUsageTest : public testing::Test {
UnsafeBufferUsageTest()
: TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
- Builder(TUSum), Extractor(TUSummaryBuilder) {}
+ Builder(TUSum), Extractor(Builder) {}
template <typename ContributorDecl = NamedDecl>
std::unique_ptr<UnsafeBufferUsageEntitySummary>
>From 9a4630ddc19dd5a166b8e18df5bdc80e3f647fc7 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Tue, 17 Mar 2026 16:45:21 -0700
Subject: [PATCH 11/51] Fix clang-format issue
---
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 2c2bd6abd7ab5..c0a4c2f76ab48 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -100,7 +100,6 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
}
bool empty() const { return UnsafeBuffers.empty(); }
-
};
} // namespace clang::ssaf
>From 50ab0797d809062257534c33918ebbbdeccc87a5 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Tue, 24 Mar 2026 17:31:01 -0700
Subject: [PATCH 12/51] [NFC][SSAF][UnsafeBufferUsage] Separate
EntityPointerLevel and UnsafeBufferUsage
EntityPointerLevel as a common data structure will later be shared by
UnsafeBufferUsage and pointer assignments analysis. So this commit
makes them separate:
- EntityPointerLevel provides the data structure and translation
- UnsafeBufferUsage uses EntityPointerLevel to translate unsafe pointers to EPLs.
---
.../Analyses/EntityPointerLevel.h | 134 +++++++
.../UnsafeBufferUsage/UnsafeBufferUsage.h | 73 +---
.../UnsafeBufferUsageExtractor.h | 5 -
.../Analyses/CMakeLists.txt | 3 +-
.../Analyses/EntityPointerLevel.cpp | 332 ++++++++++++++++++
.../UnsafeBufferUsage/UnsafeBufferUsage.cpp | 55 ++-
.../UnsafeBufferUsageExtractor.cpp | 225 +-----------
.../UnsafeBufferUsageTest.cpp | 4 +-
8 files changed, 506 insertions(+), 325 deletions(-)
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
new file mode 100644
index 0000000000000..f2a2c73a29a7a
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
@@ -0,0 +1,134 @@
+//===---------------- EntityPointerLevel.h ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
+
+#include "clang/AST/Decl.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+#include <set>
+
+namespace clang::ssaf {
+
+/// An EntityPointerLevel is associated with a level of the declared
+/// pointer/array type of an entity. In the fully-expanded spelling of the
+/// declared type, a EntityPointerLevel is associated with a '*' (or a '[]`) in
+/// that declaration.
+///
+/// For example, for 'int *p[10];', there are two EntityPointerLevels. One
+/// is associated with 'int *[10]' of 'p' and the other is associated with 'int
+/// *' of 'p'.
+///
+/// An EntityPointerLevel can be identified by an EntityId and an unsigned
+/// integer indicating the pointer level: '(EntityId, PointerLevel)'.
+/// An EntityPointerLevel 'P' is valid iff 'P.EntityId' has a pointer type with
+/// at least 'P.PointerLevel' levels (This implies 'P.PointerLevel > 0').
+///
+/// For the same example 'int *p[10];', the EntityPointerLevels below are valid:
+/// - '(p, 2)' is associated with the 'int *' part of the declared type of 'p';
+/// - '(p, 1)' is associated with the 'int *[10]' part of the declared type of
+/// 'p'.
+class EntityPointerLevel {
+ EntityId Entity;
+ unsigned PointerLevel;
+
+ friend class EntityPointerLevelTranslator;
+ friend llvm::Expected<EntityPointerLevel>
+ entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
+ // For unittests:
+ friend EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
+
+ // EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
+ // : Entity(Entity), PointerLevel(PointerLevel) {}
+ EntityPointerLevel(std::pair<EntityId, unsigned> Pair)
+ : Entity(Pair.first), PointerLevel(Pair.second) {}
+
+public:
+ EntityId getEntity() const { return Entity; }
+ unsigned getPointerLevel() const { return PointerLevel; }
+
+ bool operator==(const EntityPointerLevel &Other) const {
+ return std::tie(Entity, PointerLevel) ==
+ std::tie(Other.Entity, Other.PointerLevel);
+ }
+
+ bool operator!=(const EntityPointerLevel &Other) const {
+ return !(*this == Other);
+ }
+
+ bool operator<(const EntityPointerLevel &Other) const {
+ return std::tie(Entity, PointerLevel) <
+ std::tie(Other.Entity, Other.PointerLevel);
+ }
+
+ /// Compares `EntityPointerLevel`s; additionally, partially compares
+ /// `EntityPointerLevel` with `EntityId`.
+ struct Comparator {
+ using is_transparent = void;
+ bool operator()(const EntityPointerLevel &L,
+ const EntityPointerLevel &R) const {
+ return L < R;
+ }
+ bool operator()(const EntityId &L, const EntityPointerLevel &R) const {
+ return L < R.getEntity();
+ }
+ bool operator()(const EntityPointerLevel &L, const EntityId &R) const {
+ return L.getEntity() < R;
+ }
+ };
+};
+
+using EntityPointerLevelSet =
+ std::set<EntityPointerLevel, EntityPointerLevel::Comparator>;
+
+/// Translate a pointer/array type expression 'E' to a (set of)
+/// EntityPointerLevel(s) associated with the declared type of the base address
+/// of `E`. If the base address of `E` is not associated with an entity, the
+/// translation result is an empty set.
+///
+/// \param E the pointer expression to be translated
+/// \param Ctx the AST context of `E`
+/// \param AddEntity the callback provided by the caller to convert EntityNames
+/// to EntityIds.
+llvm::Expected<EntityPointerLevelSet>
+translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
+ std::function<EntityId(EntityName EN)> AddEntity);
+
+/// Create an EntityPointerLevel (EPL) from a NamedDecl of a pointer/array type.
+///
+/// \param E the pointer expression to be translated
+/// \param Ctx the AST context of `E`
+/// \param AddEntity the callback provided by the caller to convert EntityNames
+/// to EntityIds.
+/// \param IsFunRet true iff the created EPL is associated with the return type
+/// of a function entity.
+llvm::Expected<EntityPointerLevel>
+creatEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
+ std::function<EntityId(EntityName EN)> AddEntity,
+ bool IsFunRet = false);
+
+/// Creates a new EntityPointerLevel (EPL) from `E` by incrementing `E`'s
+/// pointer level.
+/// \return the EPL that is associated with the pointee (or array element) type
+/// of `E`'s associated pointer/array tyoe of the same entity.
+EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E);
+
+llvm::json::Value
+entityPointerLevelToJSON(const EntityPointerLevel &EPL,
+ JSONFormat::EntityIdToJSONFn EntityId2JSON);
+
+llvm::Expected<EntityPointerLevel>
+entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
+
+/// Proxy function creating EPLs for unit tests:
+EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
+} // namespace clang::ssaf
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 2b36c47fe67a5..0b9a4170f653d 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -9,81 +9,14 @@
#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
-#include <set>
namespace clang::ssaf {
-
-/// An EntityPointerLevel represents a level of the declared pointer/array
-/// type of an entity. In the fully-expanded spelling of the declared type, a
-/// EntityPointerLevel is associated with a '*' (or a '[]`) in that declaration.
-///
-/// For example, for 'int *p[10];', there are two EntityPointerLevels. One
-/// is associated with 'int *[10]' of 'p' and the other is associated with 'int
-/// *' of 'p'.
-///
-/// An EntityPointerLevel can be identified by an EntityId and an unsigned
-/// integer indicating the pointer level: '(EntityId, PointerLevel)'.
-/// An EntityPointerLevel 'P' is valid iff 'P.EntityId' has a pointer type with
-/// at least 'P.PointerLevel' levels (This implies 'P.PointerLevel > 0').
-///
-/// For the same example 'int *p[10];', the EntityPointerLevels below are valid:
-/// - '(p, 2)' is associated with the 'int *' part of the declared type of 'p';
-/// - '(p, 1)' is associated with the 'int *[10]' part of the declared type of
-/// 'p'.
-class EntityPointerLevel {
- EntityId Entity;
- unsigned PointerLevel;
-
- friend class UnsafeBufferUsageTUSummaryExtractor;
- friend class UnsafeBufferUsageEntitySummary;
-
- EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
- : Entity(Entity), PointerLevel(PointerLevel) {}
-
-public:
- EntityId getEntity() const { return Entity; }
- unsigned getPointerLevel() const { return PointerLevel; }
-
- bool operator==(const EntityPointerLevel &Other) const {
- return std::tie(Entity, PointerLevel) ==
- std::tie(Other.Entity, Other.PointerLevel);
- }
-
- bool operator!=(const EntityPointerLevel &Other) const {
- return !(*this == Other);
- }
-
- bool operator<(const EntityPointerLevel &Other) const {
- return std::tie(Entity, PointerLevel) <
- std::tie(Other.Entity, Other.PointerLevel);
- }
-
- /// Compares `EntityPointerLevel`s; additionally, partially compares
- /// `EntityPointerLevel` with `EntityId`.
- struct Comparator {
- using is_transparent = void;
- bool operator()(const EntityPointerLevel &L,
- const EntityPointerLevel &R) const {
- return L < R;
- }
- bool operator()(const EntityId &L, const EntityPointerLevel &R) const {
- return L < R.getEntity();
- }
- bool operator()(const EntityPointerLevel &L, const EntityId &R) const {
- return L.getEntity() < R;
- }
- };
-};
-
-using EntityPointerLevelSet =
- std::set<EntityPointerLevel, EntityPointerLevel::Comparator>;
-
-/// An UnsafeBufferUsageEntitySummary is an immutable set of unsafe buffers, in
-/// the form of EntityPointerLevel.
+/// An UnsafeBufferUsageEntitySummary contains a set of EntityPointerLevels
+/// extracted from unsafe buffer pointers contributed by an entity.
class UnsafeBufferUsageEntitySummary final : public EntitySummary {
const EntityPointerLevelSet UnsafeBuffers;
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
index 765b2c37562ce..13d4e18b4e81f 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
@@ -22,11 +22,6 @@ class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor {
UnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder)
: TUSummaryExtractor(Builder) {}
- static EntityPointerLevel buildEntityPointerLevel(EntityId Entity,
- unsigned PointerLevel) {
- return {Entity, PointerLevel};
- }
-
EntityId addEntity(EntityName EN) { return SummaryBuilder.addEntity(EN); }
std::unique_ptr<UnsafeBufferUsageEntitySummary>
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
index c85fa044c1e9f..c8544d073d276 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -3,8 +3,9 @@ set(LLVM_LINK_COMPONENTS
)
add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses
+ EntityPointerLevel.cpp
UnsafeBufferUsage/UnsafeBufferUsage.cpp
- UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+ UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
LINK_LIBS
clangAST
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
new file mode 100644
index 0000000000000..e607529342351
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
@@ -0,0 +1,332 @@
+//===----------------- EntityPointerLevel.cpp -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include <optional>
+
+namespace {
+using namespace clang;
+template <typename NodeTy, typename... Ts>
+static inline llvm::Error strErrAtNode(ASTContext &Ctx, const NodeTy &N,
+ StringRef Fmt, const Ts &...Args) {
+ std::string LocStr = N.getBeginLoc().printToString(Ctx.getSourceManager());
+ llvm::SmallVector<char> FmtData;
+
+ (Fmt + " at %s").toStringRef(FmtData);
+ return llvm::createStringError(FmtData.data(), Args..., LocStr.c_str());
+}
+
+static inline llvm::Error entityNameErrFor(ASTContext &Ctx,
+ const NamedDecl &D) {
+ return strErrAtNode(Ctx, D, "failed to create entity name for %s",
+ D.getNameAsString().data());
+}
+
+template <typename DeclOrExpr>
+static bool hasPtrOrArrType(const DeclOrExpr &E) {
+ return llvm::isa<PointerType>(E.getType().getCanonicalType()) ||
+ llvm::isa<ArrayType>(E.getType().getCanonicalType());
+}
+
+template <typename... Ts>
+static inline llvm::Error makeErrorSawButExpected(const llvm::json::Value &Saw,
+ llvm::StringRef Expected,
+ const Ts &...ExpectedArgs) {
+ return llvm::createStringError(
+ ("saw %s but expected " + Expected).str().c_str(),
+ llvm::formatv("{0:2}", Saw).str().data(), Expected.data(),
+ ExpectedArgs...);
+}
+} // namespace
+
+namespace clang::ssaf {
+// Translate a pointer type expression 'E' to a (set of) EntityPointerLevel(s)
+// associated with the declared type of the base address of `E`. If the base
+// address of `E` is not associated with an entity, the translation result is an
+// empty set.
+//
+// The translation is a process of traversing into the pointer 'E' until its
+// base address can be represented by an entity, with the number of dereferences
+// tracked by incrementing the pointer level. Naturally, taking address of, as
+// the inverse operation of dereference, is tracked by decrementing the pointer
+// level.
+//
+// For example, suppose there are pointers and arrays declared as
+// int *ptr, **p1, **p2;
+// int arr[10][10];
+// , the translation of expressions involving these base addresses will be:
+// Translate(ptr + 5) -> {(ptr, 1)}
+// Translate(arr[5]) -> {(arr, 2)}
+// Translate(cond ? p1[5] : p2) -> {(p1, 2), (p2, 1)}
+// Translate(&arr[5]) -> {(arr, 1)}
+class EntityPointerLevelTranslator
+ : public ConstStmtVisitor<EntityPointerLevelTranslator,
+ Expected<EntityPointerLevelSet>> {
+ friend class StmtVisitorBase;
+
+ // Fallback method for all unsupported expression kind:
+ llvm::Error fallback(const Stmt *E) {
+ return strErrAtNode(Ctx, *E,
+ "attempt to translate %s to EntityPointerLevels",
+ E->getStmtClassName());
+ }
+
+ EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) {
+ return EntityPointerLevel({AddEntity(Name), 1});
+ }
+
+ // The common helper function for Translate(*base):
+ // Translate(*base) -> Translate(base) with .pointerLevel + 1
+ Expected<EntityPointerLevelSet> translateDereferencePointer(const Expr *Ptr) {
+ assert(hasPtrOrArrType(*Ptr));
+
+ Expected<EntityPointerLevelSet> SubResult = Visit(Ptr);
+ if (!SubResult)
+ return SubResult.takeError();
+
+ auto Incremented = llvm::map_range(*SubResult, incrementPointerLevel);
+ return EntityPointerLevelSet{Incremented.begin(), Incremented.end()};
+ }
+
+ std::function<EntityId(EntityName EN)> AddEntity;
+ ASTContext &Ctx;
+
+public:
+ EntityPointerLevelTranslator(std::function<EntityId(EntityName EN)> AddEntity,
+ ASTContext &Ctx)
+ : AddEntity(AddEntity), Ctx(Ctx) {}
+
+ Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
+ Expected<EntityPointerLevel> translate(const NamedDecl *D, bool IsRet) {
+ if (IsRet && !isa<FunctionDecl>(D))
+ return strErrAtNode(
+ Ctx, *D,
+ "attempt to call getEntityNameForReturn on a NamedDecl of %s kind",
+ D->getDeclKindName());
+
+ std::optional<EntityName> EN =
+ IsRet ? getEntityNameForReturn(cast<FunctionDecl>(D))
+ : getEntityName(D);
+ if (EN)
+ return createEntityPointerLevelFor(*EN);
+ return entityNameErrFor(Ctx, *D);
+ }
+
+ static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
+ return EntityPointerLevel({E.getEntity(), E.getPointerLevel() + 1});
+ }
+
+ static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) {
+ assert(E.getPointerLevel() > 0);
+ return EntityPointerLevel({E.getEntity(), E.getPointerLevel() - 1});
+ }
+
+private:
+ Expected<EntityPointerLevelSet> VisitStmt(const Stmt *E) {
+ return fallback(E);
+ }
+
+ // Translate(base + x) -> Translate(base)
+ // Translate(x + base) -> Translate(base)
+ // Translate(base - x) -> Translate(base)
+ // Translate(base {+=, -=, =} x) -> Translate(base)
+ // Translate(x, base) -> Translate(base)
+ Expected<EntityPointerLevelSet> VisitBinaryOperator(const BinaryOperator *E) {
+ switch (E->getOpcode()) {
+ case clang::BO_Add:
+ if (hasPtrOrArrType(*E->getLHS()))
+ return Visit(E->getLHS());
+ return Visit(E->getRHS());
+ case clang::BO_Sub:
+ case clang::BO_AddAssign:
+ case clang::BO_SubAssign:
+ case clang::BO_Assign:
+ return Visit(E->getLHS());
+ case clang::BO_Comma:
+ return Visit(E->getRHS());
+ default:
+ return fallback(E);
+ }
+ }
+
+ // Translate({++, --}base) -> Translate(base)
+ // Translate(base{++, --}) -> Translate(base)
+ // Translate(*base) -> Translate(base) with .pointerLevel += 1
+ // Translate(&base) -> {}, if Translate(base) is {}
+ // -> Translate(base) with .pointerLevel -= 1
+ Expected<EntityPointerLevelSet> VisitUnaryOperator(const UnaryOperator *E) {
+ switch (E->getOpcode()) {
+ case clang::UO_PostInc:
+ case clang::UO_PostDec:
+ case clang::UO_PreInc:
+ case clang::UO_PreDec:
+ return Visit(E->getSubExpr());
+ case clang::UO_AddrOf: {
+ Expected<EntityPointerLevelSet> SubResult = Visit(E->getSubExpr());
+ if (!SubResult)
+ return SubResult.takeError();
+
+ auto Decremented = llvm::map_range(*SubResult, decrementPointerLevel);
+ return EntityPointerLevelSet{Decremented.begin(), Decremented.end()};
+ }
+ case clang::UO_Deref:
+ return translateDereferencePointer(E->getSubExpr());
+ default:
+ return fallback(E);
+ }
+ }
+
+ // Translate((T*)base) -> Translate(p) if p has pointer type
+ // -> {} otherwise
+ Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) {
+ if (hasPtrOrArrType(*E->getSubExpr()))
+ return Visit(E->getSubExpr());
+ return EntityPointerLevelSet{};
+ }
+
+ // Translate(f(...)) -> {} if it is an indirect call
+ // -> {(f_return, 1)}, otherwise
+ Expected<EntityPointerLevelSet> VisitCallExpr(const CallExpr *E) {
+ if (auto *FD = E->getDirectCallee())
+ if (auto FDEntityName = getEntityNameForReturn(FD))
+ return EntityPointerLevelSet{
+ createEntityPointerLevelFor(*FDEntityName)};
+ return EntityPointerLevelSet{};
+ }
+
+ // Translate(base[x]) -> Translate(*base)
+ Expected<EntityPointerLevelSet>
+ VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+ return translateDereferencePointer(E->getBase());
+ }
+
+ // Translate(cond ? base1 : base2) := Translate(base1) U Translate(base2)
+ Expected<EntityPointerLevelSet>
+ VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
+ Expected<EntityPointerLevelSet> ReT = Visit(E->getTrueExpr());
+ Expected<EntityPointerLevelSet> ReF = Visit(E->getFalseExpr());
+
+ if (ReT && ReF) {
+ ReT->insert(ReF->begin(), ReF->end());
+ return ReT;
+ }
+ if (!ReF && !ReT)
+ return llvm::joinErrors(ReT.takeError(), ReF.takeError());
+ if (!ReF)
+ return ReF.takeError();
+ return ReT.takeError();
+ }
+
+ Expected<EntityPointerLevelSet> VisitParenExpr(const ParenExpr *E) {
+ return Visit(E->getSubExpr());
+ }
+
+ // Translate("string-literal") -> {}
+ // Buffer accesses on string literals are unsafe, but string literals are not
+ // entities so there is no EntityPointerLevel associated with it.
+ Expected<EntityPointerLevelSet> VisitStringLiteral(const StringLiteral *E) {
+ return EntityPointerLevelSet{};
+ }
+
+ // Translate(DRE) -> {(Decl, 1)}
+ Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
+ if (auto EntityName = getEntityName(E->getDecl()))
+ return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
+ return entityNameErrFor(Ctx, *E->getDecl());
+ }
+
+ // Translate({., ->}f) -> {(MemberDecl, 1)}
+ Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
+ if (auto EntityName = getEntityName(E->getMemberDecl()))
+ return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
+ return entityNameErrFor(Ctx, *E->getMemberDecl());
+ }
+
+ // Translate(`DefaultArg`) -> Translate(`DefaultArg->getExpr()`)
+ Expected<EntityPointerLevelSet>
+ VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) {
+ return Visit(E->getExpr());
+ }
+
+ Expected<EntityPointerLevelSet>
+ VisitOpaqueValueExpr(const OpaqueValueExpr *S) {
+ return Visit(S->getSourceExpr());
+ }
+};
+
+Expected<EntityPointerLevelSet>
+translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
+ std::function<EntityId(EntityName EN)> AddEntity) {
+ EntityPointerLevelTranslator Translator(AddEntity, Ctx);
+
+ return Translator.translate(E);
+}
+
+/// Create an EntityPointerLevel from a ValueDecl of a pointer type.
+Expected<EntityPointerLevel>
+creatEntityPointerLevel(const NamedDecl *D, ASTContext &Ctx,
+ std::function<EntityId(EntityName EN)> AddEntity,
+ bool IsFunRet) {
+ EntityPointerLevelTranslator Translator(AddEntity, Ctx);
+
+ return Translator.translate(D, IsFunRet);
+}
+
+EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
+ return EntityPointerLevelTranslator::incrementPointerLevel(E);
+}
+
+// Writes an EntityPointerLevel as
+// Array [
+// Object { "@" : [entity-id]},
+// [pointer-level-integer]
+// ]
+llvm::json::Value
+entityPointerLevelToJSON(const EntityPointerLevel &EPL,
+ JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+ return llvm::json::Array{EntityId2JSON(EPL.getEntity()),
+ llvm::json::Value(EPL.getPointerLevel())};
+}
+
+Expected<EntityPointerLevel>
+entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
+ auto *AsArr = EPLData.getAsArray();
+
+ if (!AsArr || AsArr->size() != 2)
+ return makeErrorSawButExpected(
+ EPLData, "a JSON array of size 2: [EntityId, PointerLevel]");
+
+ auto *EntityIdObj = (*AsArr)[0].getAsObject();
+
+ if (!EntityIdObj)
+ return makeErrorSawButExpected((*AsArr)[0],
+ "a JSON object representing EntityID");
+
+ Expected<EntityId> Id = EntityIdFromJSON(*EntityIdObj);
+
+ if (!Id)
+ return Id.takeError();
+
+ std::optional<uint64_t> PtrLv = (*AsArr)[1].getAsInteger();
+
+ if (!PtrLv)
+ return makeErrorSawButExpected((*AsArr)[1],
+ "a JSON value representing an integer");
+
+ return EntityPointerLevel{std::tie(*Id, *PtrLv)};
+}
+
+EntityPointerLevel buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
+ return EntityPointerLevel({Id, PtrLv});
+}
+
+} // namespace clang::ssaf
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 44eaa20c74236..fd73647dc958c 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -7,10 +7,20 @@
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
namespace {
constexpr const char *const UnsafeBuffersKey = "UnsafeBuffers";
+template <typename... Ts>
+static inline llvm::Error makeErrorSawButExpected(const llvm::json::Value &Saw,
+ llvm::StringRef Expected,
+ const Ts &...ExpectedArgs) {
+ return llvm::createStringError(
+ ("saw %s but expected " + Expected).str().c_str(),
+ llvm::formatv("{0:2}", Saw).str().data(), Expected.data(),
+ ExpectedArgs...);
+}
} // namespace
namespace clang::ssaf {
@@ -18,24 +28,14 @@ using Object = llvm::json::Object;
using Array = llvm::json::Array;
using Value = llvm::json::Value;
+// Writes the summary into an array of EntityPointerLevels:
llvm::json::Object UnsafeBufferUsageEntitySummary::jsonSerializeFn(
const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityId2JSON) {
- // Writes a EntityPointerLevel as
- // Array {
- // Object {
- // "@" : [entity-id]
- // },
- // [pointer-level]
- // }
Array UnsafeBuffersData;
for (const auto &EPL :
static_cast<const UnsafeBufferUsageEntitySummary &>(ES).UnsafeBuffers)
- UnsafeBuffersData.push_back(
- Value(Array{// EntityId:
- Value(EntityId2JSON(EPL.getEntity())),
- // PointerLevel:
- Value(EPL.getPointerLevel())}));
+ UnsafeBuffersData.push_back(entityPointerLevelToJSON(EPL, EntityId2JSON));
Object Data;
@@ -47,31 +47,26 @@ llvm::Expected<std::unique_ptr<EntitySummary>>
UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
const llvm::json::Object &Data, EntityIdTable &,
JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
- const Array *UnsafeBuffersData = Data.getArray(UnsafeBuffersKey);
- constexpr const char *const ErrMsg = "unrecognized UnsafeBufferUsageEntitySummary data";
+ const Value *UnsafeBuffersData = Data.get(UnsafeBuffersKey);
if (!UnsafeBuffersData)
- return llvm::createStringError(ErrMsg);
-
- EntityPointerLevelSet UnsafeBuffers;
+ return makeErrorSawButExpected(
+ Object(Data), "a JSON object with the key: %s", UnsafeBuffersKey);
- for (auto &EltData : *UnsafeBuffersData) {
- const Array *EltDataAsArr = EltData.getAsArray();
+ const auto *AsArr = UnsafeBuffersData->getAsArray();
- if (!EltDataAsArr || EltDataAsArr->size() != 2)
- return llvm::createStringError(ErrMsg);
+ if (!AsArr)
+ return makeErrorSawButExpected(*UnsafeBuffersData,
+ "a JSON array of EntityPointerLevels");
- const Object *IdData = (*EltDataAsArr)[0].getAsObject();
- std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
-
- if (!IdData || !PtrLvData)
- return llvm::createStringError(ErrMsg);
+ EntityPointerLevelSet UnsafeBuffers;
- llvm::Expected<EntityId> Id = EntityIdFromJSON(*IdData);
+ for (auto &UnsafeBufferData : *AsArr) {
+ auto EPL = entityPointerLevelFromJSON(UnsafeBufferData, EntityIdFromJSON);
- if (!Id)
- return Id.takeError();
- UnsafeBuffers.insert(EntityPointerLevel(Id.get(), *PtrLvData));
+ if (!EPL)
+ return EPL.takeError();
+ UnsafeBuffers.insert(*EPL);
}
return std::make_unique<UnsafeBufferUsageEntitySummary>(
UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers)));
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index c609168e4dc7d..aeab9fc8867a3 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -9,14 +9,13 @@
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DynamicRecursiveASTVisitor.h"
-#include "clang/AST/StmtVisitor.h"
#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Error.h"
#include <memory>
@@ -24,22 +23,6 @@ namespace {
using namespace clang;
using namespace ssaf;
-static bool hasPointerType(const Expr *E) {
- auto Ty = E->getType();
- return !Ty.isNull() && !Ty->isFunctionPointerType() &&
- (Ty->isPointerType() || Ty->isArrayType());
-}
-
-constexpr inline auto buildEntityPointerLevel =
- UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
-
-static llvm::Error makeUnsupportedStmtKindError(const Stmt *Unsupported) {
- return llvm::createStringError(
- "unsupported expression kind for translation to "
- "EntityPointerLevel: %s",
- Unsupported->getStmtClassName());
-}
-
static llvm::Error makeCreateEntityNameError(const NamedDecl *FailedDecl,
ASTContext &Ctx) {
std::string LocStr = FailedDecl->getSourceRange().getBegin().printToString(
@@ -59,208 +42,17 @@ static llvm::Error makeAddEntitySummaryError(const NamedDecl *FailedContributor,
FailedContributor->getNameAsString().c_str(), LocStr.c_str());
}
-// Translate a pointer type expression 'E' to a (set of) EntityPointerLevel(s)
-// associated with the declared type of the base address of `E`. If the base
-// address of `E` is not associated with an entity, the translation result is an
-// empty set.
-//
-// The translation is a process of traversing into the pointer 'E' until its
-// base address can be represented by an entity, with the number of dereferences
-// tracked by incrementing the pointer level. Naturally, taking address of, as
-// the inverse operation of dereference, is tracked by decrementing the pointer
-// level.
-//
-// For example, suppose there are pointers and arrays declared as
-// int *ptr, **p1, **p2;
-// int arr[10][10];
-// , the translation of expressions involving these base addresses will be:
-// Translate(ptr + 5) -> {(ptr, 1)}
-// Translate(arr[5]) -> {(arr, 2)}
-// Translate(cond ? p1[5] : p2) -> {(p1, 2), (p2, 1)}
-// Translate(&arr[5]) -> {(arr, 1)}
-class EntityPointerLevelTranslator
- : ConstStmtVisitor<EntityPointerLevelTranslator,
- Expected<EntityPointerLevelSet>> {
- friend class StmtVisitorBase;
-
- // Fallback method for all unsupported expression kind:
- llvm::Error fallback(const Stmt *E) {
- return makeUnsupportedStmtKindError(E);
- }
-
- static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
- return buildEntityPointerLevel(E.getEntity(), E.getPointerLevel() + 1);
- }
-
- static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) {
- assert(E.getPointerLevel() > 0);
- return buildEntityPointerLevel(E.getEntity(), E.getPointerLevel() - 1);
- }
-
- EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) {
- return buildEntityPointerLevel(Extractor.addEntity(Name), 1);
- }
-
- // The common helper function for Translate(*base):
- // Translate(*base) -> Translate(base) with .pointerLevel + 1
- Expected<EntityPointerLevelSet> translateDereferencePointer(const Expr *Ptr) {
- assert(hasPointerType(Ptr));
-
- Expected<EntityPointerLevelSet> SubResult = Visit(Ptr);
- if (!SubResult)
- return SubResult.takeError();
-
- auto Incremented = llvm::map_range(*SubResult, incrementPointerLevel);
- return EntityPointerLevelSet{Incremented.begin(), Incremented.end()};
- }
-
- UnsafeBufferUsageTUSummaryExtractor &Extractor;
- ASTContext &Ctx;
-
-public:
- EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor,
- ASTContext &Ctx)
- : Extractor(Extractor), Ctx(Ctx) {}
-
- Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
-
-private:
- Expected<EntityPointerLevelSet> VisitStmt(const Stmt *E) {
- return fallback(E);
- }
-
- // Translate(base + x) -> Translate(base)
- // Translate(x + base) -> Translate(base)
- // Translate(base - x) -> Translate(base)
- // Translate(base {+=, -=, =} x) -> Translate(base)
- // Translate(x, base) -> Translate(base)
- Expected<EntityPointerLevelSet> VisitBinaryOperator(const BinaryOperator *E) {
- switch (E->getOpcode()) {
- case clang::BO_Add:
- if (hasPointerType(E->getLHS()))
- return Visit(E->getLHS());
- return Visit(E->getRHS());
- case clang::BO_Sub:
- case clang::BO_AddAssign:
- case clang::BO_SubAssign:
- case clang::BO_Assign:
- return Visit(E->getLHS());
- case clang::BO_Comma:
- return Visit(E->getRHS());
- default:
- return fallback(E);
- }
- }
-
- // Translate({++, --}base) -> Translate(base)
- // Translate(base{++, --}) -> Translate(base)
- // Translate(*base) -> Translate(base) with .pointerLevel += 1
- // Translate(&base) -> {}, if Translate(base) is {}
- // -> Translate(base) with .pointerLevel -= 1
- Expected<EntityPointerLevelSet> VisitUnaryOperator(const UnaryOperator *E) {
- switch (E->getOpcode()) {
- case clang::UO_PostInc:
- case clang::UO_PostDec:
- case clang::UO_PreInc:
- case clang::UO_PreDec:
- return Visit(E->getSubExpr());
- case clang::UO_AddrOf: {
- Expected<EntityPointerLevelSet> SubResult = Visit(E->getSubExpr());
- if (!SubResult)
- return SubResult.takeError();
-
- auto Decremented = llvm::map_range(*SubResult, decrementPointerLevel);
- return EntityPointerLevelSet{Decremented.begin(), Decremented.end()};
- }
- case clang::UO_Deref:
- return translateDereferencePointer(E->getSubExpr());
- default:
- return fallback(E);
- }
- }
-
- // Translate((T*)base) -> Translate(p) if p has pointer type
- // -> {} otherwise
- Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) {
- if (hasPointerType(E->getSubExpr()))
- return Visit(E->getSubExpr());
- return EntityPointerLevelSet{};
- }
-
- // Translate(f(...)) -> {} if it is an indirect call
- // -> {(f_return, 1)}, otherwise
- Expected<EntityPointerLevelSet> VisitCallExpr(const CallExpr *E) {
- if (auto *FD = E->getDirectCallee())
- if (auto FDEntityName = getEntityNameForReturn(FD))
- return EntityPointerLevelSet{
- createEntityPointerLevelFor(*FDEntityName)};
- return EntityPointerLevelSet{};
- }
-
- // Translate(base[x]) -> Translate(*base)
- Expected<EntityPointerLevelSet>
- VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
- return translateDereferencePointer(E->getBase());
- }
-
- // Translate(cond ? base1 : base2) := Translate(base1) U Translate(base2)
- Expected<EntityPointerLevelSet>
- VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
- Expected<EntityPointerLevelSet> ReT = Visit(E->getTrueExpr());
- Expected<EntityPointerLevelSet> ReF = Visit(E->getFalseExpr());
-
- if (ReT && ReF) {
- ReT->insert(ReF->begin(), ReF->end());
- return ReT;
- }
- if (!ReF && !ReT)
- return llvm::joinErrors(ReT.takeError(), ReF.takeError());
- if (!ReF)
- return ReF.takeError();
- return ReT.takeError();
- }
-
- Expected<EntityPointerLevelSet> VisitParenExpr(const ParenExpr *E) {
- return Visit(E->getSubExpr());
- }
-
- // Translate("string-literal") -> {}
- // Buffer accesses on string literals are unsafe, but string literals are not
- // entities so there is no EntityPointerLevel associated with it.
- Expected<EntityPointerLevelSet> VisitStringLiteral(const StringLiteral *E) {
- return EntityPointerLevelSet{};
- }
-
- // Translate(DRE) -> {(Decl, 1)}
- Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
- if (auto EntityName = getEntityName(E->getDecl()))
- return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
- return makeCreateEntityNameError(E->getDecl(), Ctx);
- }
-
- // Translate({., ->}f) -> {(MemberDecl, 1)}
- Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
- if (auto EntityName = getEntityName(E->getMemberDecl()))
- return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
- return makeCreateEntityNameError(E->getMemberDecl(), Ctx);
- }
-
- Expected<EntityPointerLevelSet>
- VisitOpaqueValueExpr(const OpaqueValueExpr *S) {
- return Visit(S->getSourceExpr());
- }
-};
-
Expected<EntityPointerLevelSet>
buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
UnsafeBufferUsageTUSummaryExtractor &Extractor,
- ASTContext &Ctx) {
+ ASTContext &Ctx,
+ std::function<EntityId(EntityName)> AddEntity) {
EntityPointerLevelSet Result{};
- EntityPointerLevelTranslator Translator{Extractor, Ctx};
llvm::Error AllErrors = llvm::ErrorSuccess();
for (const Expr *Ptr : UnsafePointers) {
- Expected<EntityPointerLevelSet> Translation = Translator.translate(Ptr);
+ Expected<EntityPointerLevelSet> Translation =
+ translateEntityPointerLevel(Ptr, Ctx, AddEntity);
if (Translation) {
// Filter out those temporary invalid EntityPointerLevels associated with
@@ -297,8 +89,9 @@ static std::set<const Expr *> findUnsafePointersInContributor(const Decl *D) {
std::unique_ptr<UnsafeBufferUsageEntitySummary>
UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary(
const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) {
+ auto AddEntity = [this](EntityName EN) { return addEntity(EN); };
Expected<EntityPointerLevelSet> EPLs = buildEntityPointerLevels(
- findUnsafePointersInContributor(Contributor), *this, Ctx);
+ findUnsafePointersInContributor(Contributor), *this, Ctx, AddEntity);
if (EPLs)
return std::make_unique<UnsafeBufferUsageEntitySummary>(
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 8b5b76b6e58b6..9efdec64ea30c 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -9,6 +9,7 @@
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/Frontend/ASTUnit.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
@@ -57,9 +58,6 @@ const FunctionDecl *findFnByName(StringRef Name, ASTContext &Ctx) {
return findDeclByName<FunctionDecl>(Name, Ctx);
}
-constexpr inline auto buildEntityPointerLevel =
- UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
-
class UnsafeBufferUsageTest : public testing::Test {
protected:
TUSummary TUSum;
>From 5e830286290b4cd1b993e2257efa1091b6ade837 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Tue, 24 Mar 2026 17:59:49 -0700
Subject: [PATCH 13/51] [SSAF][UnsafeBufferUsage] Implement AST visitor that
respects the constribution model and refactor
Previously, the UnsafeBufferUsage Extractor relied on the
`-Wunsafe-buffer-usage` API to traverse ASTs. The traversal did not
fully respect the contribution model of SSAF---RecordDecls inside
functions were not treated as contributors. Their fields were counted
as contributions of the enclosing function.
This commit adds an AST visitor that respects the contribution model
and will be shared by SSAF analyses. The UnsafeBufferUsage Extractor
still relies on `-Wunsafe-buffer-usage` to provide the unsafe pointer
matching function.
In addition, this commit
- Factors common code in analyses to 'lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h'.
- Registers the UnsafeBufferUsage extractor.
- Removes 'UnsafeBufferUsageExtractor.h' since it is useless except
for the unit test. The unit test now directly uses proxy functions
defined in 'UnsafeBufferUsageExtractor.cpp'.
---
.../Analysis/Analyses/UnsafeBufferUsage.h | 12 +-
.../UnsafeBufferUsage.h | 12 +-
.../UnsafeBufferUsageExtractor.h | 35 ---
.../SSAFBuiltinForceLinker.h | 5 +
clang/lib/Analysis/UnsafeBufferUsage.cpp | 117 ++++-----
.../Analyses/EntityPointerLevel.cpp | 37 +--
.../Analyses/SSAFAnalysesCommon.h | 146 +++++++++++
.../UnsafeBufferUsage/UnsafeBufferUsage.cpp | 19 +-
.../UnsafeBufferUsageExtractor.cpp | 235 ++++++++----------
.../UnsafeBufferUsageTest.cpp | 65 +++--
.../CMakeLists.txt | 2 +-
.../SummaryExtractorRegistryTest.cpp | 1 +
12 files changed, 384 insertions(+), 302 deletions(-)
rename clang/include/clang/ScalableStaticAnalysisFramework/Analyses/{UnsafeBufferUsage => }/UnsafeBufferUsage.h (85%)
delete mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
rename clang/unittests/ScalableStaticAnalysisFramework/Analyses/{UnsafeBufferUsage => }/UnsafeBufferUsageTest.cpp (91%)
diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
index e0d583c735e61..65b79cfc1c2ff 100644
--- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
+++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -201,14 +201,10 @@ bool anyConflict(const llvm::SmallVectorImpl<FixItHint> &FixIts,
const SourceManager &SM);
} // namespace internal
-/// Find unsafe pointers in body/initializer of `D`, if `D` is one of the
-/// followings:
-/// VarDecl
-/// FieldDecl
-/// FunctionDecl
-/// BlockDecl
-/// ObjCMethodDecl
-std::set<const Expr *> findUnsafePointers(const Decl *D);
+/// \return true iff `N` is an unsafe buffer usage and populates the unsafe
+/// pointers in `UnsafePointers`
+bool matchUnsafePointers(const DynTypedNode &N, ASTContext &Ctx,
+ std::set<const Expr *> &UnsafePointers);
} // end namespace clang
#endif /* LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H */
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage.h
similarity index 85%
rename from clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
rename to clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage.h
index 0b9a4170f653d..16180db20787f 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage.h
@@ -39,12 +39,12 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
bool empty() const { return UnsafeBuffers.empty(); }
static llvm::json::Object
- jsonSerializeFn(const EntitySummary &ES,
- JSONFormat::EntityIdToJSONFn EntityId2JSON);
+ summaryToJSON(const EntitySummary &ES,
+ JSONFormat::EntityIdToJSONFn EntityId2JSON);
static llvm::Expected<std::unique_ptr<EntitySummary>>
- jsonDeserializeFn(const llvm::json::Object &Data, EntityIdTable &,
- JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
+ summaryFromJSON(const llvm::json::Object &Data, EntityIdTable &,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
static SummaryName summaryName() { return SummaryName{"UnsafeBufferUsage"}; }
};
@@ -53,8 +53,8 @@ struct UnsafeBufferUsageJSONFormatInfo : JSONFormat::FormatInfo {
UnsafeBufferUsageJSONFormatInfo()
: JSONFormat::FormatInfo(
UnsafeBufferUsageEntitySummary::summaryName(),
- UnsafeBufferUsageEntitySummary::jsonSerializeFn,
- UnsafeBufferUsageEntitySummary::jsonDeserializeFn) {}
+ UnsafeBufferUsageEntitySummary::summaryToJSON,
+ UnsafeBufferUsageEntitySummary::summaryFromJSON) {}
};
} // namespace clang::ssaf
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
deleted file mode 100644
index 13d4e18b4e81f..0000000000000
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
+++ /dev/null
@@ -1,35 +0,0 @@
-//===- UnsafeBufferUsageExtractor.h -----------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
-
-#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
-#include "llvm/Support/Error.h"
-#include <memory>
-
-namespace clang::ssaf {
-class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor {
-public:
- UnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder)
- : TUSummaryExtractor(Builder) {}
-
- EntityId addEntity(EntityName EN) { return SummaryBuilder.addEntity(EN); }
-
- std::unique_ptr<UnsafeBufferUsageEntitySummary>
- extractEntitySummary(const Decl *Contributor, ASTContext &Ctx,
- llvm::Error &Error);
-
- void HandleTranslationUnit(ASTContext &Ctx) override;
-};
-} // namespace clang::ssaf
-
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
index e0a394da1a921..670b80e49eadc 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
@@ -29,4 +29,9 @@ extern volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource;
[[maybe_unused]] static int UnsafeBufferUsageSSAFJSONFormatAnchorDestination =
UnsafeBufferUsageSSAFJSONFormatAnchorSource;
+extern volatile int UnsafeBufferUsageTUSummaryExtractorAnchorSource;
+[[maybe_unused]] static int
+ UnsafeBufferUsageTUSummaryExtractorAnchorDestination =
+ UnsafeBufferUsageSSAFJSONFormatAnchorSource;
+
#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINFORCELINKER_H
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 5a9241acbee36..5369774db1ba5 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -2974,63 +2974,6 @@ static void populateStmtsForFindingGadgets(SmallVector<const Stmt *> &Stmts,
}
}
-std::set<const Expr *> clang::findUnsafePointers(const Decl *D) {
- class MockReporter : public UnsafeBufferUsageHandler {
- public:
- MockReporter() {}
- void handleUnsafeOperation(const Stmt *, bool, ASTContext &) override {}
- void handleUnsafeLibcCall(const CallExpr *, unsigned, ASTContext &,
- const Expr *UnsafeArg = nullptr) override {}
- void handleUnsafeOperationInContainer(const Stmt *, bool,
- ASTContext &) override {}
- void handleUnsafeVariableGroup(const VarDecl *,
- const VariableGroupsManager &, FixItList &&,
- const Decl *,
- const FixitStrategy &) override {}
- void handleUnsafeUniquePtrArrayAccess(const DynTypedNode &Node,
- bool IsRelatedToDecl,
- ASTContext &Ctx) override {}
- bool ignoreUnsafeBufferInContainer(const SourceLocation &) const override {
- return false;
- }
- bool isSafeBufferOptOut(const SourceLocation &) const override {
- return false;
- }
- bool ignoreUnsafeBufferInLibcCall(const SourceLocation &) const override {
- return false;
- }
- bool ignoreUnsafeBufferInStaticSizedArray(
- const SourceLocation &Loc) const override {
- return false;
- }
- std::string getUnsafeBufferUsageAttributeTextAt(
- SourceLocation, StringRef WSSuffix = "") const override {
- return "";
- }
- };
-
- FixableGadgetList FixableGadgets;
- WarningGadgetList WarningGadgets;
- DeclUseTracker Tracker;
- MockReporter IgnoreHandler;
- ASTContext &Ctx = D->getASTContext();
- SmallVector<const Stmt *> Stmts;
-
- populateStmtsForFindingGadgets(Stmts, D);
- for (auto *Stmt : Stmts)
- findGadgets(Stmt, Ctx, IgnoreHandler, false, FixableGadgets, WarningGadgets,
- Tracker);
-
- std::set<const Expr *> Result;
- for (auto &G : WarningGadgets) {
- for (const Expr *E : G->getUnsafePtrs()) {
- Result.insert(E);
- }
- }
-
- return Result;
-}
-
struct WarningGadgetSets {
std::map<const VarDecl *, std::set<const WarningGadget *>,
// To keep keys sorted by their locations in the map so that the
@@ -4748,3 +4691,63 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
std::move(Tracker), Handler, EmitSuggestions);
}
+
+bool clang::matchUnsafePointers(const DynTypedNode &N, ASTContext &Ctx,
+ std::set<const Expr *> &UnsafePointers) {
+ class MockReporter : public UnsafeBufferUsageHandler {
+ public:
+ MockReporter() {}
+ void handleUnsafeOperation(const Stmt *, bool, ASTContext &) override {}
+ void handleUnsafeLibcCall(const CallExpr *, unsigned, ASTContext &,
+ const Expr *UnsafeArg = nullptr) override {}
+ void handleUnsafeOperationInContainer(const Stmt *, bool,
+ ASTContext &) override {}
+ void handleUnsafeVariableGroup(const VarDecl *,
+ const VariableGroupsManager &, FixItList &&,
+ const Decl *,
+ const FixitStrategy &) override {}
+ void handleUnsafeUniquePtrArrayAccess(const DynTypedNode &Node,
+ bool IsRelatedToDecl,
+ ASTContext &Ctx) override {}
+ bool ignoreUnsafeBufferInContainer(const SourceLocation &) const override {
+ return false;
+ }
+ bool isSafeBufferOptOut(const SourceLocation &) const override {
+ return false;
+ }
+ bool ignoreUnsafeBufferInLibcCall(const SourceLocation &) const override {
+ return false;
+ }
+ bool ignoreUnsafeBufferInStaticSizedArray(
+ const SourceLocation &Loc) const override {
+ return false;
+ }
+ std::string getUnsafeBufferUsageAttributeTextAt(
+ SourceLocation, StringRef WSSuffix = "") const override {
+ return "";
+ }
+ } Handler;
+
+ const Stmt *S = N.get<Stmt>();
+ if (!S)
+ return false;
+
+ MatchResult Result;
+ WarningGadgetList WarningGadgets;
+ bool Matched = false;
+
+#define WARNING_GADGET(name) \
+ if (name##Gadget::matches(S, Ctx, Result)) \
+ WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));
+#define WARNING_OPTIONAL_GADGET(name) \
+ if (name##Gadget::matches(S, Ctx, &Handler, Result)) \
+ WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));
+#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
+
+ for (auto &WG : WarningGadgets)
+ for (auto *E : WG->getUnsafePtrs()) {
+ UnsafePointers.insert(E);
+ Matched = true;
+ }
+ return Matched;
+}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
index e607529342351..ddbc45301ac4b 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
@@ -5,48 +5,15 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "SSAFAnalysesCommon.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
#include <optional>
-namespace {
-using namespace clang;
-template <typename NodeTy, typename... Ts>
-static inline llvm::Error strErrAtNode(ASTContext &Ctx, const NodeTy &N,
- StringRef Fmt, const Ts &...Args) {
- std::string LocStr = N.getBeginLoc().printToString(Ctx.getSourceManager());
- llvm::SmallVector<char> FmtData;
-
- (Fmt + " at %s").toStringRef(FmtData);
- return llvm::createStringError(FmtData.data(), Args..., LocStr.c_str());
-}
-
-static inline llvm::Error entityNameErrFor(ASTContext &Ctx,
- const NamedDecl &D) {
- return strErrAtNode(Ctx, D, "failed to create entity name for %s",
- D.getNameAsString().data());
-}
-
-template <typename DeclOrExpr>
-static bool hasPtrOrArrType(const DeclOrExpr &E) {
- return llvm::isa<PointerType>(E.getType().getCanonicalType()) ||
- llvm::isa<ArrayType>(E.getType().getCanonicalType());
-}
-
-template <typename... Ts>
-static inline llvm::Error makeErrorSawButExpected(const llvm::json::Value &Saw,
- llvm::StringRef Expected,
- const Ts &...ExpectedArgs) {
- return llvm::createStringError(
- ("saw %s but expected " + Expected).str().c_str(),
- llvm::formatv("{0:2}", Saw).str().data(), Expected.data(),
- ExpectedArgs...);
-}
-} // namespace
-
namespace clang::ssaf {
// Translate a pointer type expression 'E' to a (set of) EntityPointerLevel(s)
// associated with the declared type of the base address of `E`. If the base
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
new file mode 100644
index 0000000000000..61e104bc8a0eb
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -0,0 +1,146 @@
+//===------------------ SSAFAnalysesCommon.h --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Common code in SSAF analyses implementations
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/ParentMapContext.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/JSON.h"
+
+namespace {
+using namespace clang;
+
+template <typename NodeTy, typename... Ts>
+static inline llvm::Error strErrAtNode(ASTContext &Ctx, const NodeTy &N,
+ StringRef Fmt, const Ts &...Args) {
+ std::string LocStr = N.getBeginLoc().printToString(Ctx.getSourceManager());
+ llvm::SmallVector<char> FmtData;
+
+ (Fmt + " at %s").toStringRef(FmtData);
+ return llvm::createStringError(FmtData.data(), Args..., LocStr.c_str());
+}
+
+static inline llvm::Error entityNameErrFor(ASTContext &Ctx,
+ const NamedDecl &D) {
+ return strErrAtNode(Ctx, D, "failed to create entity name for %s",
+ D.getNameAsString().data());
+}
+
+static inline llvm::Error failedToAddEntitySummaryFor(ASTContext &Ctx,
+ const NamedDecl *D) {
+ std::string LocStr = D->getBeginLoc().printToString(Ctx.getSourceManager());
+
+ return llvm::createStringError("failed to add entity summary for %s at %s",
+ D->getNameAsString().data(), LocStr.c_str());
+}
+
+template <typename... Ts>
+static inline llvm::Error makeErrorSawButExpected(const llvm::json::Value &Saw,
+ llvm::StringRef Expected,
+ const Ts &...ExpectedArgs) {
+ return llvm::createStringError(
+ ("saw %s but expected " + Expected).str().c_str(),
+ llvm::formatv("{0:2}", Saw).str().data(), Expected.data(),
+ ExpectedArgs...);
+}
+
+template <typename DeclOrExpr>
+static bool hasPtrOrArrType(const DeclOrExpr &E) {
+ return llvm::isa<PointerType>(E.getType().getCanonicalType()) ||
+ llvm::isa<ArrayType>(E.getType().getCanonicalType());
+}
+
+/// Traverses the AST and finds contributors:
+class ContributorFinder : public DynamicRecursiveASTVisitor {
+public:
+ std::vector<const NamedDecl *> Contributors;
+
+ bool VisitFunctionDecl(FunctionDecl *D) override {
+ Contributors.push_back(D);
+ return true;
+ }
+
+ bool VisitRecordDecl(RecordDecl *D) override {
+ Contributors.push_back(D);
+ return true;
+ }
+
+ bool VisitVarDecl(VarDecl *D) override {
+ DeclContext *DC = D->getDeclContext();
+
+ if (DC->isFileContext() || DC->isNamespace())
+ Contributors.push_back(D);
+ return false;
+ }
+};
+
+/// An AST visitor that skips callable decl and record decl strict-descendant
+/// because those are separate contributors.
+///
+/// The visitor calls
+/// `MatcherTy::matchFact(DynTypedNode &, ASTContext &, const NamedDecl
+/// *Contributor)` on each visited Decl or Stmt node to collect facts in the
+/// `Contributor`.
+template <typename MatcherTy>
+class ContributorFactFinder : public DynamicRecursiveASTVisitor {
+ ASTContext &Ctx;
+ MatcherTy &Matcher;
+ const NamedDecl *RootDecl;
+
+ template <typename T> bool match(const T &Node) {
+ return Matcher.matches(DynTypedNode::create(Node), Ctx, RootDecl);
+ }
+
+public:
+ ContributorFactFinder(ASTContext &Ctx, MatcherTy &Matcher)
+ : Ctx(Ctx), Matcher(Matcher) {
+ ShouldVisitTemplateInstantiations = true;
+ ShouldVisitImplicitCode = false;
+ }
+
+ /// The entry point:
+ void findMatches(const NamedDecl *Contributor) {
+ RootDecl = Contributor;
+ TraverseDecl(const_cast<NamedDecl *>(Contributor));
+ }
+
+ bool TraverseDecl(Decl *Node) override {
+ if (!Node)
+ return true;
+ // To skip callables:
+ if (Node != RootDecl && isa<FunctionDecl, CXXConstructorDecl, BlockDecl,
+ ObjCMethodDecl, RecordDecl>(Node))
+ return true;
+ match(*Node);
+ return DynamicRecursiveASTVisitor::TraverseDecl(Node);
+ }
+
+ bool TraverseStmt(Stmt *Node) override {
+ if (!Node)
+ return true;
+ match(*Node);
+ return DynamicRecursiveASTVisitor::TraverseStmt(Node);
+ }
+
+ bool TraverseLambdaExpr(LambdaExpr *L) override {
+ // TODO: lambda captures of pointer variables (by copy or by reference)
+ // are currently not tracked. Each capture initializes an implicit closure
+ // field from the captured variable, which constitutes a pointer assignment
+ // edge that should be recorded here.
+ return true; // skip lambda as it is a callable
+ }
+};
+} // namespace
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
\ No newline at end of file
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index fd73647dc958c..37e00b61ae2f4 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -6,21 +6,14 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage.h"
+#include "SSAFAnalysesCommon.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
+#include "llvm/Support/JSON.h"
namespace {
constexpr const char *const UnsafeBuffersKey = "UnsafeBuffers";
-template <typename... Ts>
-static inline llvm::Error makeErrorSawButExpected(const llvm::json::Value &Saw,
- llvm::StringRef Expected,
- const Ts &...ExpectedArgs) {
- return llvm::createStringError(
- ("saw %s but expected " + Expected).str().c_str(),
- llvm::formatv("{0:2}", Saw).str().data(), Expected.data(),
- ExpectedArgs...);
-}
} // namespace
namespace clang::ssaf {
@@ -29,7 +22,7 @@ using Array = llvm::json::Array;
using Value = llvm::json::Value;
// Writes the summary into an array of EntityPointerLevels:
-llvm::json::Object UnsafeBufferUsageEntitySummary::jsonSerializeFn(
+llvm::json::Object UnsafeBufferUsageEntitySummary::summaryToJSON(
const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityId2JSON) {
Array UnsafeBuffersData;
@@ -44,8 +37,8 @@ llvm::json::Object UnsafeBufferUsageEntitySummary::jsonSerializeFn(
}
llvm::Expected<std::unique_ptr<EntitySummary>>
-UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
- const llvm::json::Object &Data, EntityIdTable &,
+UnsafeBufferUsageEntitySummary::summaryFromJSON(
+ const Object &Data, EntityIdTable &,
JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
const Value *UnsafeBuffersData = Data.get(UnsafeBuffersKey);
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index aeab9fc8867a3..c023e737bac9c 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -6,157 +6,142 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
+#include "SSAFAnalysesCommon.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage.h"
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/Support/Error.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
+#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
#include <memory>
namespace {
using namespace clang;
-using namespace ssaf;
-
-static llvm::Error makeCreateEntityNameError(const NamedDecl *FailedDecl,
- ASTContext &Ctx) {
- std::string LocStr = FailedDecl->getSourceRange().getBegin().printToString(
- Ctx.getSourceManager());
- return llvm::createStringError(
- "failed to create entity name for %s declared at %s",
- FailedDecl->getNameAsString().c_str(), LocStr.c_str());
-}
-
-static llvm::Error makeAddEntitySummaryError(const NamedDecl *FailedContributor,
- ASTContext &Ctx) {
- std::string LocStr =
- FailedContributor->getSourceRange().getBegin().printToString(
- Ctx.getSourceManager());
- return llvm::createStringError(
- "failed to add entity summary for contributor %s declared at %s",
- FailedContributor->getNameAsString().c_str(), LocStr.c_str());
-}
+struct UnsafePointerMatcher {
+ std::set<const Expr *> UnsafePointers;
-Expected<EntityPointerLevelSet>
-buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
- UnsafeBufferUsageTUSummaryExtractor &Extractor,
- ASTContext &Ctx,
- std::function<EntityId(EntityName)> AddEntity) {
- EntityPointerLevelSet Result{};
- llvm::Error AllErrors = llvm::ErrorSuccess();
-
- for (const Expr *Ptr : UnsafePointers) {
- Expected<EntityPointerLevelSet> Translation =
- translateEntityPointerLevel(Ptr, Ctx, AddEntity);
-
- if (Translation) {
- // Filter out those temporary invalid EntityPointerLevels associated with
- // `&E` pointers:
- auto FilteredTranslation = llvm::make_filter_range(
- *Translation, [](const EntityPointerLevel &E) -> bool {
- return E.getPointerLevel() > 0;
- });
- Result.insert(FilteredTranslation.begin(), FilteredTranslation.end());
- continue;
- }
- AllErrors = llvm::joinErrors(std::move(AllErrors), Translation.takeError());
+ bool matches(const DynTypedNode &N, ASTContext &Ctx,
+ const NamedDecl *Contributor) {
+ return matchUnsafePointers(N, Ctx, UnsafePointers);
}
- if (AllErrors)
- return AllErrors;
- return Result;
-}
-} // namespace
+};
-static std::set<const Expr *> findUnsafePointersInContributor(const Decl *D) {
- if (isa<FunctionDecl>(D) || isa<VarDecl>(D))
- return findUnsafePointers(D);
- if (auto *RD = dyn_cast<RecordDecl>(D)) {
- std::set<const Expr *> Result;
+void findFactsInContributor(const NamedDecl *Contributor, ASTContext &Ctx,
+ std::set<const Expr *> &UnsafePointers) {
+ UnsafePointerMatcher Matcher;
+ ContributorFactFinder<UnsafePointerMatcher> Finder(Ctx, Matcher);
- for (const FieldDecl *FD : RD->fields()) {
- Result.merge(findUnsafePointers(FD));
- }
- return Result;
- }
- return {};
+ Finder.findMatches(Contributor);
+ UnsafePointers.merge(Matcher.UnsafePointers);
}
+} // namespace
-std::unique_ptr<UnsafeBufferUsageEntitySummary>
-UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary(
- const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) {
- auto AddEntity = [this](EntityName EN) { return addEntity(EN); };
- Expected<EntityPointerLevelSet> EPLs = buildEntityPointerLevels(
- findUnsafePointersInContributor(Contributor), *this, Ctx, AddEntity);
+namespace clang::ssaf {
- if (EPLs)
- return std::make_unique<UnsafeBufferUsageEntitySummary>(
- UnsafeBufferUsageEntitySummary(std::move(*EPLs)));
- Error = EPLs.takeError();
- return nullptr;
-}
+class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor {
+public:
+ UnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder)
+ : TUSummaryExtractor(Builder) {}
-void UnsafeBufferUsageTUSummaryExtractor::HandleTranslationUnit(
- ASTContext &Ctx) {
+ EntityId addEntity(EntityName EN) { return SummaryBuilder.addEntity(EN); }
- // FIXME: I suppose finding contributor Decls is commonly needed by all/many
- // extractors
- class ContributorFinder : public DynamicRecursiveASTVisitor {
- public:
- std::vector<const NamedDecl *> Contributors;
+ Expected<std::unique_ptr<UnsafeBufferUsageEntitySummary>>
+ extractEntitySummary(const NamedDecl *Contributor, ASTContext &Ctx) {
+ std::set<const Expr *> UnsafePointers;
+ EntityPointerLevelSet Results;
- bool VisitFunctionDecl(FunctionDecl *D) override {
- Contributors.push_back(D);
- return true;
- }
+ findFactsInContributor(Contributor, Ctx, UnsafePointers);
+ for (const Expr *Ptr : UnsafePointers) {
+ Expected<EntityPointerLevelSet> Translation =
+ translateEntityPointerLevel(Ptr, Ctx, [this](const EntityName &EN) {
+ return SummaryBuilder.addEntity(EN);
+ });
- bool VisitRecordDecl(RecordDecl *D) override {
- Contributors.push_back(D);
- return true;
+ if (Translation) {
+ // Filter out those temporary invalid EntityPointerLevels associated
+ // with
+ // `&E` pointers. They need no transformation of entities:
+ auto FilteredTranslation = llvm::make_filter_range(
+ *Translation, [](const EntityPointerLevel &E) -> bool {
+ return E.getPointerLevel() > 0;
+ });
+ Results.insert(FilteredTranslation.begin(), FilteredTranslation.end());
+ continue;
+ }
+ return Translation.takeError();
}
+ return std::make_unique<UnsafeBufferUsageEntitySummary>(
+ UnsafeBufferUsageEntitySummary(std::move(Results)));
+ }
- bool VisitVarDecl(VarDecl *D) override {
- DeclContext *DC = D->getDeclContext();
-
- if (DC->isFileContext() || DC->isNamespace())
- Contributors.push_back(D);
- return false;
+ void HandleTranslationUnit(ASTContext &Ctx) override {
+ llvm::Error Errors = llvm::ErrorSuccess();
+ auto addError = [&Errors](llvm::Error Err) {
+ Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
+ };
+ ContributorFinder ContributorFinder;
+
+ ContributorFinder.VisitTranslationUnitDecl(Ctx.getTranslationUnitDecl());
+ for (auto *CD : ContributorFinder.Contributors) {
+ auto EntitySummary = extractEntitySummary(CD, Ctx);
+
+ if (!EntitySummary) {
+ addError(EntitySummary.takeError());
+ continue;
+ }
+ assert(*EntitySummary &&
+ "std::unique_ptr<EntitySummary> should not be null");
+ if ((*EntitySummary)->empty())
+ continue;
+
+ auto ContributorName = getEntityName(CD);
+
+ if (!ContributorName) {
+ addError(entityNameErrFor(Ctx, *CD));
+ continue;
+ }
+
+ auto [EntitySummaryPtr, Success] = SummaryBuilder.addSummary(
+ addEntity(*ContributorName), std::move(*EntitySummary));
+
+ if (!Success)
+ addError(failedToAddEntitySummaryFor(Ctx, CD));
}
- } ContributorFinder;
-
- ContributorFinder.VisitTranslationUnitDecl(Ctx.getTranslationUnitDecl());
-
- llvm::Error Errors = llvm::ErrorSuccess();
- auto addError = [&Errors](llvm::Error Err) {
- Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
- };
-
- for (auto *CD : ContributorFinder.Contributors) {
- llvm::Error Error = llvm::ErrorSuccess();
- auto EntitySummary = extractEntitySummary(CD, Ctx, Error);
-
- if (Error)
- addError(std::move(Error));
- if (EntitySummary->empty())
- continue;
+ // FIXME: handle errors!
+ llvm::consumeError(std::move(Errors));
+ }
+};
- auto ContributorName = getEntityName(CD);
+// Proxy functions for unit tests:
+extern Expected<std::unique_ptr<UnsafeBufferUsageEntitySummary>>
+extractEntitySummary(UnsafeBufferUsageTUSummaryExtractor &Extractor,
+ const NamedDecl *Contributor, ASTContext &Ctx) {
+ return Extractor.extractEntitySummary(Contributor, Ctx);
+}
- if (!ContributorName) {
- addError(makeCreateEntityNameError(CD, Ctx));
- continue;
- }
+extern UnsafeBufferUsageTUSummaryExtractor *
+createUnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder) {
+ return new UnsafeBufferUsageTUSummaryExtractor(Builder);
+}
- auto [EntitySummaryPtr, Success] = SummaryBuilder.addSummary(
- addEntity(*ContributorName), std::move(EntitySummary));
+extern void destroyUnsafeBufferUsageTUSummaryExtractor(
+ UnsafeBufferUsageTUSummaryExtractor *Extractor) {
+ delete Extractor;
+}
- if (!Success)
- addError(makeAddEntitySummaryError(CD, Ctx));
- }
- // FIXME: handle errors!
- llvm::consumeError(std::move(Errors));
+extern EntityId addEntity(UnsafeBufferUsageTUSummaryExtractor &Extractor,
+ const EntityName &EN) {
+ return Extractor.addEntity(EN);
}
+} // namespace clang::ssaf
+
+volatile int UnsafeBufferUsageTUSummaryExtractorAnchorSource = 0;
+static clang::ssaf::TUSummaryExtractorRegistry::Add<
+ ssaf::UnsafeBufferUsageTUSummaryExtractor>
+ RegisterExtractor("UnsafeBufferUsageTUSummaryExtractor",
+ "The TUSummaryExtractor for unsafe buffer pointers");
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsageTest.cpp
similarity index 91%
rename from clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
rename to clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsageTest.cpp
index 9efdec64ea30c..57e9135f2e730 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsageTest.cpp
@@ -6,11 +6,9 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/Frontend/ASTUnit.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
@@ -21,13 +19,31 @@
#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include <memory>
using namespace clang;
using namespace ssaf;
using testing::UnorderedElementsAre;
-namespace {
+namespace clang::ssaf {
+// Proxy functions
+class UnsafeBufferUsageTUSummaryExtractor;
+
+extern Expected<std::unique_ptr<UnsafeBufferUsageEntitySummary>>
+extractEntitySummary(UnsafeBufferUsageTUSummaryExtractor &Extractor,
+ const NamedDecl *Contributor, ASTContext &Ctx);
+
+extern UnsafeBufferUsageTUSummaryExtractor *
+createUnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder);
+
+extern void destroyUnsafeBufferUsageTUSummaryExtractor(
+ UnsafeBufferUsageTUSummaryExtractor *Extractor);
+extern EntityId addEntity(UnsafeBufferUsageTUSummaryExtractor &Extractor,
+ const EntityName &EN);
+} // namespace clang::ssaf
+
+namespace {
template <typename SomeDecl = NamedDecl>
const SomeDecl *findDeclByName(StringRef Name, ASTContext &Ctx) {
class NamedDeclFinder : public DynamicRecursiveASTVisitor {
@@ -62,12 +78,17 @@ class UnsafeBufferUsageTest : public testing::Test {
protected:
TUSummary TUSum;
TUSummaryBuilder Builder;
- UnsafeBufferUsageTUSummaryExtractor Extractor;
+ UnsafeBufferUsageTUSummaryExtractor *Extractor;
std::unique_ptr<ASTUnit> AST;
UnsafeBufferUsageTest()
: TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
- Builder(TUSum), Extractor(Builder) {}
+ Builder(TUSum),
+ Extractor(createUnsafeBufferUsageTUSummaryExtractor(Builder)) {}
+
+ ~UnsafeBufferUsageTest() {
+ destroyUnsafeBufferUsageTUSummaryExtractor(Extractor);
+ }
template <typename ContributorDecl = NamedDecl>
std::unique_ptr<UnsafeBufferUsageEntitySummary>
@@ -75,7 +96,7 @@ class UnsafeBufferUsageTest : public testing::Test {
AST = tooling::buildASTFromCodeWithArgs(
Code, {"-Wno-unused-value", "-Wno-int-to-pointer-cast"});
- const auto *ContributorDefn =
+ auto *ContributorDefn =
findDeclByName<ContributorDecl>(ContributorName, AST->getASTContext());
if (!ContributorDefn)
@@ -86,28 +107,28 @@ class UnsafeBufferUsageTest : public testing::Test {
if (!EN)
return nullptr;
- llvm::Error Error = llvm::ErrorSuccess();
- auto Sum = Extractor.extractEntitySummary(ContributorDefn,
- AST->getASTContext(), Error);
+ auto Sum =
+ extractEntitySummary(*Extractor, ContributorDefn, AST->getASTContext());
- if (Error) {
- llvm::consumeError(std::move(Error));
+ if (!Sum) {
+ llvm::consumeError(Sum.takeError());
return nullptr;
}
- return Sum;
+ assert(*Sum);
+ return std::move(*Sum);
}
std::optional<EntityId> getEntityId(StringRef Name) {
if (const auto *D = findDeclByName(Name, AST->getASTContext()))
if (auto EntityName = getEntityName(D))
- return Extractor.addEntity(*EntityName);
+ return addEntity(*Extractor, *EntityName);
return std::nullopt;
}
std::optional<EntityId> getEntityIdForReturn(StringRef FunName) {
if (const auto *D = findFnByName(FunName, AST->getASTContext()))
if (auto EntityName = getEntityNameForReturn(D))
- return Extractor.addEntity(*EntityName);
+ return addEntity(*Extractor, *EntityName);
return std::nullopt;
}
@@ -147,8 +168,8 @@ getSubsetOf(const EntityPointerLevelSet &Set, EntityId Entity) {
}
TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
- EntityId E1 = Extractor.addEntity({"c:@F at foo", "", {}});
- EntityId E2 = Extractor.addEntity({"c:@F at bar", "", {}});
+ EntityId E1 = addEntity(*Extractor, {"c:@F at foo", "", {}});
+ EntityId E2 = addEntity(*Extractor, {"c:@F at bar", "", {}});
auto P1 = buildEntityPointerLevel(E1, 2);
auto P2 = buildEntityPointerLevel(E1, 2);
@@ -166,9 +187,9 @@ TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
}
TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
- EntityId E1 = Extractor.addEntity({"c:@F at foo", "", {}});
- EntityId E2 = Extractor.addEntity({"c:@F at bar", "", {}});
- EntityId E3 = Extractor.addEntity({"c:@F at baz", "", {}});
+ EntityId E1 = addEntity(*Extractor, {"c:@F at foo", "", {}});
+ EntityId E2 = addEntity(*Extractor, {"c:@F at bar", "", {}});
+ EntityId E3 = addEntity(*Extractor, {"c:@F at baz", "", {}});
auto P1 = buildEntityPointerLevel(E1, 1);
auto P2 = buildEntityPointerLevel(E1, 2);
@@ -260,7 +281,7 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
using Value = llvm::json::Value;
std::map<EntityId, uint64_t> DummyTable{{*getEntityId("p"), 42},
{*getEntityId("q"), 108}};
- Object JData = UnsafeBufferUsageEntitySummary::jsonSerializeFn(
+ Object JData = UnsafeBufferUsageEntitySummary::summaryToJSON(
*Sum, [&DummyTable](EntityId Id) {
return Object{{"@", Value(DummyTable[Id])}};
});
@@ -296,7 +317,7 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageDeserializeTest) {
ASSERT_NE(ParsedJSON->getAsObject(), nullptr);
EntityIdTable Ignored;
- auto ParsedSum = UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
+ auto ParsedSum = UnsafeBufferUsageEntitySummary::summaryFromJSON(
*ParsedJSON->getAsObject(), Ignored,
[&DummyTable](const Object &O) -> Expected<EntityId> {
return DummyTable.at(O.getInteger("@").value());
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
index f541e81d42789..5da36d071be32 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -1,5 +1,5 @@
add_distinct_clang_unittest(ClangScalableAnalysisTests
- Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+ Analyses/UnsafeBufferUsageTest.cpp
ASTEntityMappingTest.cpp
BuildNamespaceTest.cpp
EntityIdTableTest.cpp
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp
index 2018beebd53da..28f4d089b5764 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp
@@ -42,6 +42,7 @@ TEST(SummaryExtractorRegistryTest, EnumeratingRegistryEntries) {
"MockSummaryExtractor1",
"MockSummaryExtractor2",
"NoOpExtractor",
+ "UnsafeBufferUsageTUSummaryExtractor",
}));
}
>From cd394f79d9dd95fc43bb417d4d87722625f6f79c Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 25 Mar 2026 17:44:15 -0700
Subject: [PATCH 14/51] [SSAF][PointerAssignments] Add PointerAssignments
summary and extractor
Implement PointerAssignments summary and extractor, which uses
EntityPointerLevel. An assignment is extracted as a pair of
EntityPointerLevels. Extracted assignments form a directed graph
encoding abstracted pointer flow information.
rdar://172429193
---
.../Analyses/EntityPointerLevel.h | 20 +-
.../Analyses/PointerAssignments.h | 64 +
.../SSAFBuiltinForceLinker.h | 19 +-
clang/lib/Analysis/UnsafeBufferUsage.cpp | 20 +-
.../Analyses/CMakeLists.txt | 2 +
.../Analyses/EntityPointerLevel.cpp | 17 +-
.../PointerAssignments/PointerAssignments.cpp | 105 ++
.../PointerAssignmentsExtractor.cpp | 383 ++++++
.../Analyses/SSAFAnalysesCommon.h | 7 +-
.../UnsafeBufferUsage/tu-summary.json | 40 +
.../Analyses/PointerAssignmentsTest.cpp | 1150 +++++++++++++++++
.../CMakeLists.txt | 1 +
.../SummaryExtractorRegistryTest.cpp | 1 +
13 files changed, 1806 insertions(+), 23 deletions(-)
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignments.cpp
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignmentsExtractor.cpp
create mode 100644 clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerAssignmentsTest.cpp
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
index f2a2c73a29a7a..1ccb78066786b 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
@@ -101,24 +101,24 @@ llvm::Expected<EntityPointerLevelSet>
translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
std::function<EntityId(EntityName EN)> AddEntity);
-/// Create an EntityPointerLevel (EPL) from a NamedDecl of a pointer/array type.
+/// Create EntityPointerLevel(s) (EPLs) from a NamedDecl of a pointer/array type.
///
-/// \param E the pointer expression to be translated
+/// \param ND the pointer type Decl to be translated
/// \param Ctx the AST context of `E`
/// \param AddEntity the callback provided by the caller to convert EntityNames
/// to EntityIds.
-/// \param IsFunRet true iff the created EPL is associated with the return type
-/// of a function entity.
-llvm::Expected<EntityPointerLevel>
+/// \param IsFunRet true iff the created EPL(s) is associated with the return
+/// type of a function entity.
+llvm::Expected<EntityPointerLevelSet>
creatEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
std::function<EntityId(EntityName EN)> AddEntity,
bool IsFunRet = false);
-/// Creates a new EntityPointerLevel (EPL) from `E` by incrementing `E`'s
-/// pointer level.
-/// \return the EPL that is associated with the pointee (or array element) type
-/// of `E`'s associated pointer/array tyoe of the same entity.
-EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E);
+/// Creates new EntityPointerLevel(s) (EPLs) from the provided
+/// one(s) by incrementing their pointer levels.
+/// \return the EPL(s) that is associated with the pointee (or array element)
+/// type of `E`'s associated pointer/array tyoe of the same entity.
+EntityPointerLevelSet incrementPointerLevel(const EntityPointerLevelSet &E);
llvm::json::Value
entityPointerLevelToJSON(const EntityPointerLevel &EPL,
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h
new file mode 100644
index 0000000000000..cc82f2479a47d
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h
@@ -0,0 +1,64 @@
+//===---------------- PointerAssignments.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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines an analysis that builds directed graphs where nodes
+// are pointers and edges are assignment operations, each of which bridges two
+// nodes.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERASSIGNMENTS_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERASSIGNMENTS_H
+
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+
+namespace clang::ssaf {
+
+/// Maps LHSs to their RHS sets:
+using EdgeSet = std::map<EntityPointerLevel, EntityPointerLevelSet>;
+
+class PointerAssignmentsEntitySummary final : public EntitySummary {
+ EdgeSet Edges;
+
+ friend class PointerAssignmentsTUSummaryExtractor;
+
+ PointerAssignmentsEntitySummary(EdgeSet Edges)
+ : EntitySummary(), Edges(std::move(Edges)) {}
+
+public:
+ SummaryName getSummaryName() const override { return summaryName(); };
+
+ bool operator==(const EdgeSet &Other) const { return Edges == Other; }
+
+ bool operator==(const PointerAssignmentsEntitySummary &Other) const {
+ return Edges == Other.Edges;
+ }
+
+ bool empty() const { return Edges.empty() && Edges.empty(); }
+
+ static llvm::json::Object
+ summaryToJSON(const EntitySummary &ES,
+ JSONFormat::EntityIdToJSONFn EntityId2JSON);
+
+ static llvm::Expected<std::unique_ptr<EntitySummary>>
+ summaryFromJSON(const llvm::json::Object &Data, EntityIdTable &,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
+
+ static SummaryName summaryName() { return SummaryName{"PointerAssignments"}; }
+};
+
+struct PointerAssignmentsJSONFormatInfo : JSONFormat::FormatInfo {
+ PointerAssignmentsJSONFormatInfo()
+ : JSONFormat::FormatInfo(
+ PointerAssignmentsEntitySummary::summaryName(),
+ PointerAssignmentsEntitySummary::summaryToJSON,
+ PointerAssignmentsEntitySummary::summaryFromJSON) {}
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERASSIGNMENTS_H
\ No newline at end of file
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
index 670b80e49eadc..c7d9444b07bed 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
@@ -25,13 +25,30 @@ extern volatile int SSAFJSONFormatAnchorSource;
[[maybe_unused]] static int SSAFJSONFormatAnchorDestination =
SSAFJSONFormatAnchorSource;
+// This anchor is used to force the linker to link the UnsafeBufferUsage
+// JSONFormat registration:
extern volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource;
[[maybe_unused]] static int UnsafeBufferUsageSSAFJSONFormatAnchorDestination =
UnsafeBufferUsageSSAFJSONFormatAnchorSource;
+// This anchor is used to force the linker to link the UnsafeBufferUsage
+// JSONFormat registration:
extern volatile int UnsafeBufferUsageTUSummaryExtractorAnchorSource;
[[maybe_unused]] static int
UnsafeBufferUsageTUSummaryExtractorAnchorDestination =
- UnsafeBufferUsageSSAFJSONFormatAnchorSource;
+ UnsafeBufferUsageTUSummaryExtractorAnchorSource;
+
+// This anchor is used to force the linker to link the PointerAssignments
+// JSONFormat registration:
+extern volatile int PointerAssignmentsSSAFJSONFormatAnchorSource;
+[[maybe_unused]] static int PointerAssignmentsSSAFJSONFormatAnchorDestination =
+ PointerAssignmentsSSAFJSONFormatAnchorSource;
+
+// This anchor is used to force the linker to link the PointerAssignments
+// TUSummaryExtractor registration.
+extern volatile int PointerAssignmentsTUSummaryExtractorAnchorSource;
+[[maybe_unused]] static int
+ PointerAssignmentsTUSummaryExtractorAnchorDestination =
+ PointerAssignmentsTUSummaryExtractorAnchorSource;
#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINFORCELINKER_H
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 5369774db1ba5..2b11af5e4e458 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -2951,7 +2951,9 @@ template <typename NodeTy> struct CompareNode {
// FunctionDecl
// BlockDecl
// ObjCMethodDecl
-static void populateStmtsForFindingGadgets(SmallVector<const Stmt *> &Stmts,
+//
+// return false iff `D` is not any one above.
+static bool populateStmtsForFindingGadgets(SmallVector<const Stmt *> &Stmts,
const Decl *D) {
auto AddStmt = [&Stmts](const Stmt *S) {
if (S)
@@ -2965,13 +2967,21 @@ static void populateStmtsForFindingGadgets(SmallVector<const Stmt *> &Stmts,
llvm::append_range(
Stmts, llvm::map_range(CtorD->inits(),
std::mem_fn(&CXXCtorInitializer::getInit)));
- } else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
+ return true;
+ }
+ if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
AddStmt(D->getBody());
- } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
- AddStmt(VD->getInit()); // FIXME: default arg for ParmVarDecl?
- } else if (const auto *FD = dyn_cast<FieldDecl>(D)) {
+ return true;
+ }
+ if (const auto *VD = dyn_cast<VarDecl>(D)) {
+ AddStmt(VD->getInit());
+ return true;
+ }
+ if (const auto *FD = dyn_cast<FieldDecl>(D)) {
AddStmt(FD->getInClassInitializer());
+ return true;
}
+ return false;
}
struct WarningGadgetSets {
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
index c8544d073d276..9197d8689620e 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -6,6 +6,8 @@ add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses
EntityPointerLevel.cpp
UnsafeBufferUsage/UnsafeBufferUsage.cpp
UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+ PointerAssignments/PointerAssignments.cpp
+ PointerAssignments/PointerAssignmentsExtractor.cpp
LINK_LIBS
clangAST
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
index ddbc45301ac4b..e28e0a130a702 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
@@ -12,6 +12,8 @@
#include "clang/AST/DeclCXX.h"
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "llvm/ADT/STLExtras.h"
+#include <functional>
#include <optional>
namespace clang::ssaf {
@@ -238,17 +240,24 @@ translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
}
/// Create an EntityPointerLevel from a ValueDecl of a pointer type.
-Expected<EntityPointerLevel>
+Expected<EntityPointerLevelSet>
creatEntityPointerLevel(const NamedDecl *D, ASTContext &Ctx,
std::function<EntityId(EntityName EN)> AddEntity,
bool IsFunRet) {
EntityPointerLevelTranslator Translator(AddEntity, Ctx);
+ auto EPL = Translator.translate(D, IsFunRet);
- return Translator.translate(D, IsFunRet);
+ if (!EPL)
+ return EPL.takeError();
+ return EntityPointerLevelSet{*EPL};
}
-EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
- return EntityPointerLevelTranslator::incrementPointerLevel(E);
+EntityPointerLevelSet incrementPointerLevel(const EntityPointerLevelSet &EPLs) {
+ EntityPointerLevelSet Result;
+
+ for (auto &E : EPLs)
+ Result.insert(EntityPointerLevelTranslator::incrementPointerLevel(E));
+ return Result;
}
// Writes an EntityPointerLevel as
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignments.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignments.cpp
new file mode 100644
index 0000000000000..8f998ec35abc2
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignments.cpp
@@ -0,0 +1,105 @@
+//===---------- PointerAssignments.cpp -----------------------------------===//
+//
+// 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/PointerAssignments.h"
+#include "SSAFAnalysesCommon.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/JSON.h"
+
+namespace {
+constexpr const char *const PointerAssignmentsKey = "PointerAssignments";
+} // namespace
+
+namespace clang::ssaf {
+using Object = llvm::json::Object;
+using Array = llvm::json::Array;
+using Value = llvm::json::Value;
+
+// 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], ...]
+// ...
+// ]
+llvm::json::Object PointerAssignmentsEntitySummary::summaryToJSON(
+ const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+ Array EdgesData;
+
+ for (const auto &Entry :
+ static_cast<const PointerAssignmentsEntitySummary &>(ES).Edges) {
+ 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)));
+ }
+
+ Object Data;
+
+ Data[PointerAssignmentsKey] = Value(std::move(EdgesData));
+ return Data;
+}
+
+llvm::Expected<std::unique_ptr<EntitySummary>>
+PointerAssignmentsEntitySummary::summaryFromJSON(
+ const Object &Data, EntityIdTable &,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
+ const Value *EdgesData = Data.get(PointerAssignmentsKey);
+
+ if (!EdgesData)
+ return makeErrorSawButExpected(
+ Object(Data), "a JSON object with the key: %s", PointerAssignmentsKey);
+
+ EdgeSet Edges;
+ const auto *EdgesDataAsArr = EdgesData->getAsArray();
+
+ if (!EdgesDataAsArr)
+ return makeErrorSawButExpected(
+ *EdgesData, "a JSON array of arary of EntityPointerLevels");
+ for (const auto &EdgesEntryData : *EdgesDataAsArr) {
+ const auto *EPLArray = EdgesEntryData.getAsArray();
+
+ if (!EPLArray || EPLArray->size() <= 1)
+ return makeErrorSawButExpected(
+ EdgesEntryData, "a JSON array of EntityPointerLevels with a size "
+ "greater than 1: [lhs, rhs, rhs, ...]");
+
+ llvm::Error Err = llvm::Error::success();
+ auto EPLs = llvm::map_range(
+ *EPLArray, [&Err, &EntityIdFromJSON](const auto &EPLData) {
+ auto EPL = entityPointerLevelFromJSON(EPLData, EntityIdFromJSON);
+
+ if (!EPL)
+ Err = llvm::joinErrors(std::move(Err), EPL.takeError());
+ return *EPL;
+ });
+
+ if (Err)
+ return Err;
+ Edges[*EPLs.begin()].insert(EPLs.begin() + 1, EPLs.end());
+ }
+ return std::make_unique<PointerAssignmentsEntitySummary>(
+ PointerAssignmentsEntitySummary(std::move(Edges)));
+}
+
+static llvm::Registry<JSONFormat::FormatInfo>::Add<
+ PointerAssignmentsJSONFormatInfo>
+ RegisterPointerAssignmentsJSONFormatInfo(
+ "PointerAssignments",
+ "JSON Format info for PointerAssignmentsEntitySummary");
+
+} // namespace clang::ssaf
+
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int PointerAssignmentsSSAFJSONFormatAnchorSource = 0;
\ No newline at end of file
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignmentsExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignmentsExtractor.cpp
new file mode 100644
index 0000000000000..a49e2ffdab441
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignmentsExtractor.cpp
@@ -0,0 +1,383 @@
+//===---- PointerAssignmentsExtractor.cpp --------------------------------===//
+//
+// 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 "SSAFAnalysesCommon.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/TypeBase.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
+#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
+#include "llvm/Support/Error.h"
+#include <functional>
+#include <memory>
+#include <optional>
+
+namespace {
+using namespace clang;
+using namespace ssaf;
+
+class PointerAssignmentMatcher {
+ ASTContext &Ctx;
+ std::function<EntityId(const EntityName &)> AddEntity;
+
+ // Convert a Expr/NamedDecl to an EntityPointerLevel(Set):
+ Expected<EntityPointerLevelSet> toEPL(const NamedDecl *N,
+ bool IsRet = false) {
+ auto Ret = creatEntityPointerLevel(N, Ctx, AddEntity, IsRet);
+
+ if (Ret)
+ return EntityPointerLevelSet{*Ret};
+ return Ret.takeError();
+ }
+
+ Expected<EntityPointerLevelSet> toEPL(const Expr *N, bool IsRet = false) {
+ return translateEntityPointerLevel(N, Ctx, AddEntity);
+ }
+
+ template <typename T1, typename T2>
+ bool addEdges(const T1 *LHS, const T2 *RHS) {
+ return addEdges(toEPL(LHS), RHS, false);
+ }
+
+ template <typename T>
+ bool addEdges(Expected<EntityPointerLevelSet> &&LHS, const T *RHS,
+ bool IsRHSRet = false) {
+ auto Rs = toEPL(RHS, IsRHSRet);
+ bool Error = false;
+
+ if (!Rs) {
+ addError(Rs.takeError());
+ Error = true;
+ }
+ if (!LHS) {
+ addError(LHS.takeError());
+ Error = true;
+ }
+ if (Error)
+ return false;
+ for (auto L : *LHS)
+ Results[L].insert(Rs->begin(), Rs->end());
+ return true;
+ }
+
+ template <typename ParmsProvider, typename ArgsProvider>
+ bool matchArgsWithParams(unsigned ArgIdxStart, ParmsProvider *PP,
+ ArgsProvider *AP) {
+ bool Matched = false;
+ unsigned ArgIdx = ArgIdxStart;
+
+ for (unsigned ParmIdx = 0; ParmIdx < PP->getNumParams();
+ ++ArgIdx, ++ParmIdx) {
+ if (const ParmVarDecl *PD = PP->getParamDecl(ParmIdx);
+ PD && hasPtrOrArrType(*PD)) {
+ addEdges(PD, AP->getArg(ArgIdx));
+ Matched = true;
+ }
+ }
+ return Matched;
+ }
+
+ void addError(llvm::Error &&E) {
+ Error = llvm::joinErrors(std::move(Error), std::move(E));
+ }
+
+ // Match initializer lists of the form 'Var = {a, b, c, ...}':
+ //
+ // If 'Var' is a struct/union:
+ // 'Var = {a, b, c, ...}' => 'Var.field_1 = a'
+ // 'Var.field_2 = b'
+ // ...
+ // If 'Var' is an array:
+ // 'Var = {a, b, c, ...}' => '*Var = a'
+ // '*Var = b'
+ // ...
+ //
+ // The process is recursive: 'a', 'b', 'c', etc. may themselves be
+ // initializer lists. We therefore use `ArrayIndirectLevel` to keep track.
+ bool matchInitializerList(const ValueDecl *Base, const Expr *InitExpr,
+ unsigned ArrayElementIndirectLevel = 0) {
+ const InitListExpr *ILE = dyn_cast<InitListExpr>(InitExpr);
+
+ if (!ILE) {
+ if (!hasPtrOrArrType(*InitExpr))
+ return false;
+
+ auto Expected = toEPL(Base);
+
+ if (!Expected) {
+ addError(Expected.takeError());
+ return false;
+ }
+
+ EntityPointerLevelSet LHS = *Expected;
+
+ for (unsigned I = 0; I < ArrayElementIndirectLevel; ++I)
+ LHS = incrementPointerLevel(LHS);
+ return addEdges(LHS, InitExpr);
+ }
+ // Note that `Base`'s type is NOT the real LHS type when
+ // ArrayElementIndirectLevel > 0:
+ QualType Type = InitExpr->getType();
+
+ if (auto *RD = Type->getAsRecordDecl())
+ return matchInitializerListForRecordDecl(RD, ILE);
+ if (Type->isArrayType())
+ return matchInitializerListForArray(Base, ILE, ArrayElementIndirectLevel);
+ // Must be the case of using a initializer-list for a scalar:
+ return matchInitializerList(Base, ILE->getInit(0));
+ }
+
+ // Helper function for matchInitializerList that handles record:
+ bool matchInitializerListForRecordDecl(const RecordDecl *RecordTy,
+ const InitListExpr *ILE) {
+ if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RecordTy))
+ if (CXXRD->getNumBases() != 0) {
+ // FIXME: support this:
+ addError(strErrAtNode(
+ Ctx, *ILE,
+ "attempt to create pointer assignment edges between "
+ "CXXRecordDecls with base classes and initializer-lists"));
+ return false;
+ }
+ // Handle union:
+ if (RecordTy->isUnion()) {
+ auto *InitField = ILE->getInitializedFieldInUnion();
+
+ if (!InitField)
+ return false;
+ assert(!ILE->inits().empty());
+ return matchInitializerList(InitField, ILE->getInit(0));
+ }
+ // Handle struct/class:
+ ILE = ILE->getSemanticForm() ? ILE->getSemanticForm() : ILE;
+
+ auto FieldIter = RecordTy->field_begin();
+ bool Matched = false;
+
+ assert(RecordTy->getNumFields() >= ILE->getNumInits());
+ for (auto *Init : ILE->inits()) {
+ if (matchInitializerList(*(FieldIter++), Init))
+ Matched = true;
+ }
+ return Matched;
+ }
+
+ // Helper function for matchInitializerList that handles array:
+ bool matchInitializerListForArray(const ValueDecl *Array,
+ const InitListExpr *ILE,
+ unsigned ArrayIndirectLevel = 0) {
+ bool Matched = false;
+
+ for (auto *E : ILE->inits())
+ if (matchInitializerList(Array, E, ArrayIndirectLevel + 1))
+ Matched = true;
+ return Matched;
+ }
+
+public:
+ llvm::Error Error;
+ EdgeSet Results;
+
+ PointerAssignmentMatcher(
+ ASTContext &Ctx, std::function<EntityId(const EntityName &)> AddEntity)
+ : Ctx(Ctx), AddEntity(AddEntity), Error(llvm::Error::success()) {}
+
+ // Match and extract assignments.
+ // The extraction function 'XF' can be described by the following rules:
+ //
+ // XF(l = r) => '(toEPL(l), toEPL(r))'
+ // XF(foo(a, b, ...)) => XF(Param_1 = a), XF(Param_2 = b), ...
+ // XF(return e;) => XF(Fun = e), where 'Fun' is the enclosing
+ // function
+ // XF(ctor(a, ...) : x1(y1), ... {...})
+ // => XF(Param_1 = a), ...,
+ // XF(x1 = y1), ...,
+ // ctor's body will be visited later.
+ // XF(T var = e) => XF(Var = e)
+ // XF(T var = init-list) => see `matchInitializerList`
+ bool matches(const DynTypedNode &DynNode, ASTContext &Ctx,
+ const NamedDecl *RootDecl) {
+ if (const Stmt *S = DynNode.get<Stmt>()) {
+ // Match 'p = q' whenever it has pointer or array type:
+ if (const auto *BO = dyn_cast<BinaryOperator>(S);
+ BO && BO->getOpcode() == BO_Assign && hasPtrOrArrType(*BO)) {
+ return addEdges(BO->getLHS(), BO->getRHS());
+ }
+
+ // Match arg-to-param passing (in CallExpr) for any pointer type argument:
+ if (const auto *CE = dyn_cast<CallExpr>(S)) {
+ const FunctionDecl *FD = CE->getDirectCallee();
+
+ if (!FD)
+ return false;
+
+ unsigned ArgIdx = 0;
+
+ if (isa<CXXOperatorCallExpr>(CE))
+ if (auto *MD = dyn_cast<CXXMethodDecl>(FD);
+ MD && !MD->isExplicitObjectMemberFunction())
+ ArgIdx = 1;
+ return matchArgsWithParams(ArgIdx, FD, CE);
+ }
+ // Match arg-to-param passing (in CXXConstructExpr) for any pointer type
+ // argument:
+ if (const auto *CCE = dyn_cast<CXXConstructExpr>(S)) {
+ return matchArgsWithParams(/*ArgIdxStart=*/0, CCE->getConstructor(),
+ CCE);
+ }
+ if (const auto *RS = dyn_cast<ReturnStmt>(S)) {
+ const Expr *RetExpr = RS->getRetValue();
+ if (!hasPtrOrArrType(*RetExpr))
+ return false;
+ return addEdges(toEPL(RootDecl, true), RetExpr, false);
+ }
+ }
+
+ if (const Decl *D = DynNode.get<Decl>()) {
+ const Expr *InitExpr = nullptr;
+
+ if (const auto *VD = dyn_cast<ValueDecl>(D)) {
+ if (const auto *Var = dyn_cast<VarDecl>(VD))
+ InitExpr = Var->getInit();
+ if (const auto *Fd = dyn_cast<FieldDecl>(VD))
+ InitExpr = Fd->getInClassInitializer();
+
+ // Match initializer-list:
+ if (auto *InitLst = dyn_cast_or_null<InitListExpr>(InitExpr))
+ return matchInitializerList(VD, InitLst);
+
+ // Match initializers to variables/fields of a pointer type:
+ if (InitExpr && hasPtrOrArrType(*VD))
+ return addEdges(VD, InitExpr);
+ }
+
+ // Match C++ constructor member-initializers:
+ if (const auto *CtorD = dyn_cast<CXXConstructorDecl>(D)) {
+ bool Matched = false;
+
+ for (auto *E : CtorD->inits()) {
+ if (E->isDelegatingInitializer())
+ return matches(DynTypedNode::create(*E->getInit()), Ctx, RootDecl);
+ if (const FieldDecl *FD = E->getMember();
+ FD && hasPtrOrArrType(*FD)) {
+ addEdges(E->getMember(), E->getInit());
+ Matched = true;
+ }
+ }
+ return Matched;
+ }
+ }
+ return false;
+ }
+};
+} // namespace
+
+namespace clang::ssaf {
+class PointerAssignmentsTUSummaryExtractor : public TUSummaryExtractor {
+public:
+ PointerAssignmentsTUSummaryExtractor(TUSummaryBuilder &Builder)
+ : TUSummaryExtractor(Builder) {}
+
+ EntityId addEntity(const EntityName &EN) {
+ return SummaryBuilder.addEntity(EN);
+ }
+
+ Expected<std::unique_ptr<PointerAssignmentsEntitySummary>>
+ extractEntitySummary(const NamedDecl *Contributor, ASTContext &Ctx) {
+ PointerAssignmentMatcher Matcher(
+ Ctx, [this](const EntityName &EN) { return addEntity(EN); });
+ ContributorFactFinder<PointerAssignmentMatcher> Finder(Ctx, Matcher);
+
+ Finder.findMatches(const_cast<NamedDecl *>(Contributor));
+ if (Matcher.Error)
+ return std::move(Matcher.Error);
+ return std::make_unique<PointerAssignmentsEntitySummary>(
+ PointerAssignmentsEntitySummary(std::move(Matcher.Results)));
+ }
+
+ void HandleTranslationUnit(ASTContext &Ctx) override;
+};
+
+void PointerAssignmentsTUSummaryExtractor::HandleTranslationUnit(
+ ASTContext &Ctx) {
+ llvm::Error Errors = llvm::ErrorSuccess();
+ auto addError = [&Errors](llvm::Error Err) {
+ Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
+ };
+ ContributorFinder ContributorFinder;
+
+ ContributorFinder.VisitTranslationUnitDecl(Ctx.getTranslationUnitDecl());
+ for (auto *CD : ContributorFinder.Contributors) {
+ auto EntitySummary = extractEntitySummary(CD, Ctx);
+
+ if (!EntitySummary) {
+ addError(EntitySummary.takeError());
+ continue;
+ }
+ assert(*EntitySummary &&
+ "std::unique_ptr<EntitySummary> should not be null");
+ if ((*EntitySummary)->empty())
+ continue;
+
+ auto ContributorName = getEntityName(CD);
+
+ if (!ContributorName) {
+ addError(entityNameErrFor(Ctx, *CD));
+ continue;
+ }
+
+ auto [EntitySummaryPtr, Success] = SummaryBuilder.addSummary(
+ addEntity(*ContributorName), std::move(*EntitySummary));
+
+ if (!Success)
+ addError(failedToAddEntitySummaryFor(Ctx, CD));
+ }
+ // FIXME: handle errors!
+ llvm::consumeError(std::move(Errors));
+}
+
+// Proxy functions for unit test:
+extern Expected<std::unique_ptr<PointerAssignmentsEntitySummary>>
+extractEntitySummary(PointerAssignmentsTUSummaryExtractor &Extractor,
+ const NamedDecl *Contributor, ASTContext &Ctx) {
+ return Extractor.extractEntitySummary(Contributor, Ctx);
+}
+
+extern PointerAssignmentsTUSummaryExtractor *
+createPointerAssignmentsTUSummaryExtractor(TUSummaryBuilder &Builder) {
+ return new PointerAssignmentsTUSummaryExtractor(
+ PointerAssignmentsTUSummaryExtractor(Builder));
+}
+
+extern void deletePointerAssignmentsTUSummaryExtractor(
+ PointerAssignmentsTUSummaryExtractor *Ptr) {
+ delete Ptr;
+}
+
+extern EntityId addEntity(PointerAssignmentsTUSummaryExtractor &Extractor,
+ EntityName &EN) {
+ return Extractor.addEntity(EN);
+}
+} // namespace clang::ssaf
+
+volatile int PointerAssignmentsTUSummaryExtractorAnchorSource = 0;
+static TUSummaryExtractorRegistry::Add<PointerAssignmentsTUSummaryExtractor>
+ RegisterExtractor("PointerAssignmentsTUSummaryExtractor",
+ "The TUSummaryExtractor for pointer assignments");
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
index 61e104bc8a0eb..d600c540996cd 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -14,6 +14,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/ParentMapContext.h"
+#include "clang/AST/TypeBase.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Twine.h"
@@ -56,8 +57,7 @@ static inline llvm::Error makeErrorSawButExpected(const llvm::json::Value &Saw,
ExpectedArgs...);
}
-template <typename DeclOrExpr>
-static bool hasPtrOrArrType(const DeclOrExpr &E) {
+template <typename TypedObj> static bool hasPtrOrArrType(const TypedObj &E) {
return llvm::isa<PointerType>(E.getType().getCanonicalType()) ||
llvm::isa<ArrayType>(E.getType().getCanonicalType());
}
@@ -143,4 +143,5 @@ class ContributorFactFinder : public DynamicRecursiveASTVisitor {
}
};
} // namespace
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
\ No newline at end of file
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
+
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json
index 382b294ccc7bd..d4c2aa93373c5 100644
--- a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json
@@ -1,5 +1,45 @@
{
"data": [
+ {
+ "summary_data": [
+ {
+ "entity_id": 2,
+ "entity_summary": {
+ "PointerAssignments": [
+ [
+ [
+ {
+ "@": 0
+ },
+ 2
+ ],
+ [
+ {
+ "@": 1
+ },
+ 1
+ ]
+ ],
+ [
+ [
+ {
+ "@": 2
+ },
+ 1
+ ],
+ [
+ {
+ "@": 0
+ },
+ 1
+ ]
+ ]
+ ]
+ }
+ }
+ ],
+ "summary_name": "PointerAssignments"
+ },
{
"summary_data": [
{
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerAssignmentsTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerAssignmentsTest.cpp
new file mode 100644
index 0000000000000..790a344986f5e
--- /dev/null
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerAssignmentsTest.cpp
@@ -0,0 +1,1150 @@
+//===- PointerAssignmentsTest.cpp
+//------------------------------------------===//
+//
+// 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/PointerAssignments.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+#include <memory>
+#include <type_traits>
+#include <variant>
+
+using namespace clang;
+using namespace ssaf;
+
+namespace clang::ssaf {
+/////////////////////////////////////////////////////
+// Declare Proxy functions
+/////////////////////////////////////////////////////
+class PointerAssignmentsTUSummaryExtractor;
+
+extern Expected<std::unique_ptr<PointerAssignmentsEntitySummary>>
+extractEntitySummary(PointerAssignmentsTUSummaryExtractor &Extractor,
+ const NamedDecl *Contributor, ASTContext &Ctx);
+
+extern PointerAssignmentsTUSummaryExtractor *
+createPointerAssignmentsTUSummaryExtractor(TUSummaryBuilder &Builder);
+
+extern void deletePointerAssignmentsTUSummaryExtractor(
+ PointerAssignmentsTUSummaryExtractor *);
+
+extern EntityId addEntity(PointerAssignmentsTUSummaryExtractor &Extractor,
+ EntityName &EN);
+
+class PointerAssignmentsTUSummaryExtractorProxy {
+ PointerAssignmentsTUSummaryExtractor *Ptr;
+
+public:
+ explicit PointerAssignmentsTUSummaryExtractorProxy(TUSummaryBuilder &Builder)
+ : Ptr(createPointerAssignmentsTUSummaryExtractor(Builder)) {}
+ ~PointerAssignmentsTUSummaryExtractorProxy() {
+ deletePointerAssignmentsTUSummaryExtractor(Ptr);
+ }
+
+ PointerAssignmentsTUSummaryExtractor &operator*() const { return *Ptr; }
+};
+} // namespace clang::ssaf
+
+namespace {
+// Use FindEntityByName to identify entities in unit tests.
+// Unit tests are simple enough to meet the following assumptions:
+// - Named declarations should have unique names, they can be found by comparing
+// names with strings;
+// - Lambdas should initialize a variable named "X", they can be found using
+// "LambdaOfVar("X")";
+// - CXX Ctors should have unique combination of names and number of parameters,
+// they can be found using "CXXCtorOfNumParms(name, numParms)".
+struct LambdaOfVar {
+ StringRef VarName;
+};
+
+struct CXXCtorOfNumParms {
+ StringRef CXXCtorName;
+ unsigned NumParms;
+};
+
+using FindEntityByName =
+ std::variant<StringRef, CXXCtorOfNumParms, LambdaOfVar>;
+
+template <typename... Ts> struct Overloaded : Ts... {
+ using Ts::operator()...;
+};
+template <typename... Ts> Overloaded(Ts...) -> Overloaded<Ts...>;
+
+StringRef toStringRef(const FindEntityByName &N) {
+ return std::visit(
+ Overloaded{
+ [](StringRef S) -> StringRef { return S; },
+ [](const CXXCtorOfNumParms &L) -> StringRef { return L.CXXCtorName; },
+ [](const LambdaOfVar &L) -> StringRef { return L.VarName; },
+ },
+ N);
+}
+
+const NamedDecl *matchNamedDeclByFindEntityByName(const FindEntityByName &N,
+ const NamedDecl *D) {
+ if (std::holds_alternative<LambdaOfVar>(N))
+ D->dump();
+ return std::visit(
+ Overloaded{
+ [&D](StringRef S) -> const NamedDecl * {
+ if (D->getNameAsString() == S)
+ return D;
+ return nullptr;
+ },
+ [&D](const CXXCtorOfNumParms &L) -> const NamedDecl * {
+ if (auto *CD = dyn_cast<CXXConstructorDecl>(D)) {
+ if (CD->getNameAsString() == L.CXXCtorName &&
+ CD->getNumParams() == L.NumParms)
+ return D;
+ }
+ return nullptr;
+ },
+ [&D](const LambdaOfVar &L) -> const NamedDecl * {
+ if (const auto *VD = dyn_cast<VarDecl>(D); VD && VD->getInit()) {
+ VD->dump();
+ if (isa<LambdaExpr>(VD->getInit()) &&
+ VD->getNameAsString() == L.VarName)
+ return cast<LambdaExpr>(VD->getInit())->getCallOperator();
+ }
+ return nullptr;
+ },
+ },
+ N);
+}
+
+template <typename SomeDecl = NamedDecl,
+ typename = std::enable_if_t<std::is_base_of_v<NamedDecl, SomeDecl>>>
+const SomeDecl *findEntityByName(FindEntityByName Name, ASTContext &Ctx) {
+ class NamedDeclFinder : public DynamicRecursiveASTVisitor {
+ public:
+ FindEntityByName SearchingName;
+ const SomeDecl *FoundDecl = nullptr;
+
+ NamedDeclFinder(FindEntityByName SearchingName)
+ : SearchingName(SearchingName) {}
+
+ bool VisitDecl(Decl *D) override {
+ if (auto *ND = dyn_cast<NamedDecl>(D)) {
+ FoundDecl = llvm::dyn_cast_or_null<SomeDecl>(
+ matchNamedDeclByFindEntityByName(SearchingName, ND));
+ if (FoundDecl)
+ return false;
+ }
+ return true;
+ }
+ };
+
+ NamedDeclFinder Finder(Name);
+
+ Finder.TraverseDecl(Ctx.getTranslationUnitDecl());
+ return dyn_cast_or_null<SomeDecl>(Finder.FoundDecl);
+}
+
+const FunctionDecl *findFnByName(FindEntityByName Name, ASTContext &Ctx) {
+ return findEntityByName<FunctionDecl>(Name, Ctx);
+}
+
+// Same as `std::pair<StringName, unsigned>` for a pair of entity declaration
+// name and a pointer level with an extra optional flag for whether the entity
+// represents a function return value. This structure is used to explicitly
+// spell out components of an EPL such as "{"p", 1}" or "{"foo_fn", 2, true}".
+struct EPLPair {
+ EPLPair(FindEntityByName Name, unsigned Lv, bool isFunRet = false)
+ : Name(Name), Lv(Lv), isFunRet(isFunRet) {}
+
+ FindEntityByName Name;
+ unsigned Lv;
+ bool isFunRet;
+};
+
+class PointerAssignmentsTest : public testing::Test {
+protected:
+ TUSummary TUSum;
+ TUSummaryBuilder Builder;
+ PointerAssignmentsTUSummaryExtractorProxy ExtractorProxy;
+ std::unique_ptr<ASTUnit> AST;
+
+ PointerAssignmentsTest()
+ : TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
+ Builder(TUSum), ExtractorProxy(Builder) {}
+
+ template <typename ContributorDecl = NamedDecl,
+ typename =
+ std::enable_if_t<std::is_base_of_v<NamedDecl, ContributorDecl>>>
+ std::unique_ptr<PointerAssignmentsEntitySummary>
+ setUpTest(StringRef Code, FindEntityByName ContributorName) {
+ AST = tooling::buildASTFromCodeWithArgs(
+ Code, {"-Wno-unused-value", "-Wno-int-to-pointer-cast"});
+
+ const auto *ContributorDefn = findEntityByName<ContributorDecl>(
+ ContributorName, AST->getASTContext());
+
+ if (!ContributorDefn)
+ return nullptr;
+
+ std::optional<EntityName> EN = getEntityName(ContributorDefn);
+
+ if (!EN)
+ return nullptr;
+
+ auto Sum = extractEntitySummary(*ExtractorProxy, ContributorDefn,
+ AST->getASTContext());
+ if (!Sum) {
+ llvm::consumeError(std::move(Sum.takeError()));
+ return nullptr;
+ }
+ assert(*Sum);
+ return std::move(*Sum);
+ }
+
+public:
+ std::optional<EntityId> getEntityId(FindEntityByName Name) {
+ if (const auto *D = findEntityByName(Name, AST->getASTContext())) {
+ if (auto EntityName = getEntityName(D))
+ return addEntity(*ExtractorProxy, *EntityName);
+ }
+ return std::nullopt;
+ }
+
+ std::optional<EntityId> getEntityIdForReturn(FindEntityByName FunName) {
+ if (const auto *D = findFnByName(FunName, AST->getASTContext())) {
+ if (auto EntityName = getEntityNameForReturn(D))
+ return addEntity(*ExtractorProxy, *EntityName);
+ }
+ return std::nullopt;
+ }
+
+ EdgeSet makeEdges(unsigned Line, ArrayRef<std::pair<EPLPair, EPLPair>> Edges);
+};
+
+// 'ToEPL(Test, Line)' is a lambda that converts a 'EPLPair' to a
+// 'EntityPointerLevel':
+static constexpr auto ToEPL =
+ [](PointerAssignmentsTest *Test,
+ unsigned Line) -> std::function<EntityPointerLevel(const EPLPair &)> {
+ return [Test, Line](const EPLPair &Pair) -> EntityPointerLevel {
+ std::optional<EntityId> Entity = Pair.isFunRet
+ ? Test->getEntityIdForReturn(Pair.Name)
+ : Test->getEntityId(Pair.Name);
+ if (!Entity) {
+ ADD_FAILURE_AT(__FILE__, Line)
+ << "Entity not found: " << toStringRef(Pair.Name);
+ }
+ return buildEntityPointerLevel(*Entity, Pair.Lv);
+ };
+};
+
+EdgeSet
+PointerAssignmentsTest::makeEdges(unsigned Line,
+ ArrayRef<std::pair<EPLPair, EPLPair>> Edges) {
+ EdgeSet Result;
+ for (auto Edge : Edges)
+ Result[ToEPL(this, Line)(Edge.first)].insert(
+ ToEPL(this, Line)(Edge.second));
+ return Result;
+}
+
+TEST_F(PointerAssignmentsTest, IsExtractorRegisteredTest) {
+ EXPECT_TRUE(
+ isTUSummaryExtractorRegistered("PointerAssignmentsTUSummaryExtractor"));
+}
+
+TEST_F(PointerAssignmentsTest, IsJSONFormatRegistered) {
+ std::set<llvm::StringRef> ActualNames;
+ for (const auto &Entry :
+ llvm::Registry<clang::ssaf::JSONFormat::FormatInfo>::entries()) {
+ bool Inserted = ActualNames.insert(Entry.getName()).second;
+ EXPECT_TRUE(Inserted);
+ }
+
+ EXPECT_TRUE(ActualNames.count("PointerAssignments") == 1);
+}
+
+//////////////////////////////////////////////////////////////
+// JSON Tests //
+//////////////////////////////////////////////////////////////
+// Oracle JSON output for the example:
+// void foo(int ***p, int ****q, int x, int ****r) {
+// p[5][5][5];
+// q[5][5][5][5];
+// q[x] = p;
+// r = q;
+// }
+constexpr const char *const SerilizationTestOracle = R"cpp({
+ "PointerAssignments": [
+ [
+ [
+ {
+ "@": 108
+ },
+ 2
+ ],
+ [
+ {
+ "@": 42
+ },
+ 1
+ ]
+ ],
+ [
+ [
+ {
+ "@": 9
+ },
+ 1
+ ],
+ [
+ {
+ "@": 108
+ },
+ 1
+ ]
+ ]
+ ]
+})cpp";
+
+TEST_F(PointerAssignmentsTest, SerializeTest) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int ***p, int ****q, int x, int ****r) {
+ p[5][5][5];
+ q[5][5][5][5];
+ q[x] = p;
+ r = q;
+ }
+ )cpp",
+ "foo");
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ /*[0]=*/{{"q", 2U}, {"p", 1U}},
+ /*[1]=*/{{"r", 1U}, {"q", 1U}},
+ }));
+
+ using Object = llvm::json::Object;
+ using Value = llvm::json::Value;
+ std::map<EntityId, uint64_t> DummyTable{{*getEntityId("p"), 42},
+ {*getEntityId("q"), 108},
+ {*getEntityId("r"), 9}};
+
+ Object JData = PointerAssignmentsEntitySummary::summaryToJSON(
+ *Sum, [&DummyTable](EntityId Id) {
+ return Object{{"@", Value(DummyTable[Id])}};
+ });
+
+ EXPECT_EQ(llvm::formatv("{0:2}", llvm::json::Value(std::move(JData))).str(),
+ SerilizationTestOracle);
+}
+
+TEST_F(PointerAssignmentsTest, DeserializeTest) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int ***p, int ****q, int x, int ****r) {
+ p[5][5][5];
+ q[5][5][5][5];
+ q[x] = p;
+ r = q;
+ }
+ )cpp",
+ "foo");
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ /*[0]=*/{{"q", 2U}, {"p", 1U}},
+ /*[1]=*/{{"r", 1U}, {"q", 1U}},
+ }));
+
+ using Object = llvm::json::Object;
+ using Value = llvm::json::Value;
+ std::map<uint64_t, EntityId> DummyTable{{42, *getEntityId("p")},
+ {108, *getEntityId("q")},
+ {9, *getEntityId("r")}};
+ Expected<Value> ParsedJSON = llvm::json::parse(SerilizationTestOracle);
+
+ ASSERT_THAT_EXPECTED(ParsedJSON, llvm::Succeeded());
+ ASSERT_NE(ParsedJSON->getAsObject(), nullptr);
+
+ EntityIdTable Ignored;
+ auto ParsedSum = PointerAssignmentsEntitySummary::summaryFromJSON(
+ *ParsedJSON->getAsObject(), Ignored,
+ [&DummyTable](const Object &O) -> Expected<EntityId> {
+ return DummyTable.at(O.getInteger("@").value());
+ });
+
+ ASSERT_THAT_EXPECTED(ParsedSum, llvm::Succeeded());
+ EXPECT_EQ(*static_cast<PointerAssignmentsEntitySummary *>(ParsedSum->get()),
+ *Sum);
+}
+
+//////////////////////////////////////////////////////////////
+// Simple Assign Tests //
+//////////////////////////////////////////////////////////////
+TEST_F(PointerAssignmentsTest, SimpleAssign) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q) {
+ q = p;
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, AssignWithSubscriptLHS) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int **q, int *p, int x) {
+ q[x] = p;
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 2U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, AssignWithPtrArithRHS) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q) {
+ q = p + 5;
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, AssignInSubscript) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q) {
+ (q = p)[5];
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, MultipleAssign) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q, int *r) {
+ q = p;
+ r = q;
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"q", 1U}, {"p", 1U}},
+ {{"r", 1U}, {"q", 1U}},
+ }));
+}
+
+TEST_F(PointerAssignmentsTest, ChainedAssign) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q, int *r) {
+ r = q = p;
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"q", 1U}, {"p", 1U}},
+ {{"r", 1U}, {"q", 1U}},
+ }));
+}
+
+TEST_F(PointerAssignmentsTest, CastToRValue) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q) {
+ q = static_cast<int *&&>(p);
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, AssignToMember) {
+ auto Sum = setUpTest(R"cpp(
+ struct S { int *field; };
+ void foo(S s, int *p) {
+ s.field = p;
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"field", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, AssignToMember2) {
+ auto Sum = setUpTest(R"cpp(
+ struct S { int *field; };
+ void foo(S *s, int *p) {
+ s->field = p;
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"field", 1U}, {"p", 1U}}}));
+}
+
+//////////////////////////////////////////////////////////////
+// Call Expr Tests. //
+//////////////////////////////////////////////////////////////
+TEST_F(PointerAssignmentsTest, CallArg) {
+ auto Sum = setUpTest(R"cpp(
+ void bar(int *param);
+ void foo(int *p) {
+ bar(p);
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"param", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, CallMultiArgs) {
+ auto Sum = setUpTest(R"cpp(
+ void bar(int *param1, int y, int *param2);
+ void foo(int *p, int x, int *q) {
+ bar(p, x, q);
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"param1", 1U}, {"p", 1U}},
+ {{"param2", 1U}, {"q", 1U}},
+ }));
+}
+
+TEST_F(PointerAssignmentsTest, CallAsCallArg) {
+ auto Sum = setUpTest(R"cpp(
+
+ int *bar(int * w);
+ void foo(int * p) {
+ foo(bar(p));
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"w", 1U}, {"p", 1U}},
+ {{"p", 1U}, {"bar", 1U, true}}}));
+}
+
+TEST_F(PointerAssignmentsTest, CXXOperatorCallMultiArgs) {
+ auto Sum = setUpTest(R"cpp(
+ struct S {
+ int* operator()(int *a, int *b);
+ };
+ void foo(S obj, int *p, int *q) {
+ foo(obj, obj(p, q), obj(p, q));
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"a", 1U}, {"p", 1U}},
+ {{"b", 1U}, {"q", 1U}},
+ {{"p", 1U}, {"operator()", 1U, true}},
+ {{"q", 1U}, {"operator()", 1U, true}},
+ }));
+}
+
+TEST_F(PointerAssignmentsTest, CXXMemberCall) {
+ auto Sum = setUpTest(R"cpp(
+ struct S {
+ int* method(int *a, int *b);
+ };
+ void foo(S obj, int *p, int *q) {
+ foo(obj, obj.method(p, q), obj.method(p, q));
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"a", 1U}, {"p", 1U}},
+ {{"b", 1U}, {"q", 1U}},
+ {{"p", 1U}, {"method", 1U, true}},
+ {{"q", 1U}, {"method", 1U, true}}}));
+}
+
+TEST_F(PointerAssignmentsTest, VirtualMethodCall) {
+ auto Sum = setUpTest(R"cpp(
+ struct Base {
+ virtual void method(int *a);
+ };
+ void foo(Base &obj, int *p) {
+ obj.method(p);
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"a", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, StaticMethodCall) {
+ auto Sum = setUpTest(R"cpp(
+ struct S {
+ static void method(int *a, int *b);
+ };
+ void foo(int *p, int *q) {
+ S::method(p, q);
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"a", 1U}, {"p", 1U}},
+ {{"b", 1U}, {"q", 1U}},
+ }));
+}
+
+TEST_F(PointerAssignmentsTest, DefaultArg) {
+ auto Sum = setUpTest(R"cpp(
+ int *g;
+ void bar(int *a, int *b = g);
+ void foo(int *p) {
+ bar(p);
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__,
+ {{{"a", 1U}, {"p", 1U}}, {{"b", 1U}, {"g", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, DefaultArg2) {
+ auto Sum = setUpTest(R"cpp(
+ int *g;
+ void bar(int *a, int *b = g);
+ void foo(int *p) {
+ bar(p, p);
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"a", 1U}, {"p", 1U}},
+ {{"b", 1U}, {"p", 1U}},
+ }));
+}
+
+//////////////////////////////////////////////////////////////
+// CXX Ctor Tests. //
+//////////////////////////////////////////////////////////////
+TEST_F(PointerAssignmentsTest, CXXCtorCallMultiArgs) {
+ auto Sum = setUpTest(R"cpp(
+ struct S {
+ S(int *a, int *b) {}
+ };
+ void foo(int *p, int *q) {
+ S s{p, q};
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"a", 1U}, {"p", 1U}},
+ {{"b", 1U}, {"q", 1U}},
+ }));
+}
+
+TEST_F(PointerAssignmentsTest, CXXCtorCallMultiArgs2) {
+ auto Sum = setUpTest(R"cpp(
+ struct S {
+ S(int *a, int x, int *b) {}
+ };
+ void foo(int *p, int x, int *q) {
+ S s{p, x, q};
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"a", 1U}, {"p", 1U}},
+ {{"b", 1U}, {"q", 1U}},
+ }));
+}
+
+TEST_F(PointerAssignmentsTest, CXXCtorCallAsCallArg) {
+ auto Sum = setUpTest(R"cpp(
+ struct Wrapper {
+ Wrapper(int *q) {}
+ };
+ void bar(Wrapper w);
+ void foo(int *p) {
+ bar(Wrapper{p});
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, DelegatingCXXCtorCall) {
+ auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
+ struct S {
+ S(int *a, int *b) {}
+ S(int *p) : S(p, p) {}
+ };
+ )cpp",
+ CXXCtorOfNumParms{"S", 1});
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"a", 1U}, {"p", 1U}},
+ {{"b", 1U}, {"p", 1U}},
+ }));
+}
+
+TEST_F(PointerAssignmentsTest, CXXCtorBaseInit) {
+ auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
+ struct Base {
+ Base(int *a) {}
+ };
+ struct Derived : Base {
+ Derived(int *p) : Base(p) {}
+ };
+ )cpp",
+ "Derived");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"a", 1U}, {"p", 1U}}}));
+}
+
+//////////////////////////////////////////////////////////////
+// Initializers Tests. //
+//////////////////////////////////////////////////////////////
+TEST_F(PointerAssignmentsTest, LocalVarDeclInit) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p) {
+ int *q = p;
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, LocalVarDeclInit2) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int (*arr)[10]) {
+ int (*p)[10] = arr;
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"p", 1U}, {"arr", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, FieldInit) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p) {
+ struct Bar {
+ int *field = p;
+ };
+ }
+ )cpp",
+ "Bar");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"field", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, CXXCtorMemberInit) {
+ StringRef Code = R"cpp(
+ void foo(int *p) {
+ struct Bar {
+ int *member;
+ Bar(int *q) : member(q) {}
+ };
+ Bar B{p};
+ }
+ )cpp";
+
+ auto Sum = setUpTest<CXXConstructorDecl>(Code, "Bar");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"member", 1U}, {"q", 1U}}}));
+
+ Sum = setUpTest(Code, "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, GlobalVarInit) {
+ auto Sum = setUpTest<VarDecl>(R"cpp(
+ int *q;
+ int *g = q;
+ )cpp",
+ "g");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"g", 1U}, {"q", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, StaticLocalInit) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p) {
+ static int *s = p;
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"s", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, StaticMemberInit) {
+ auto Sum = setUpTest<VarDecl>(R"cpp(
+ int *g;
+ struct S { static int *member = g; };
+ )cpp",
+ "member");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"member", 1U}, {"g", 1U}}}));
+}
+
+//////////////////////////////////////////////////////////////
+// InitList Tests. //
+//////////////////////////////////////////////////////////////
+
+TEST_F(PointerAssignmentsTest, ArrayInitList) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q) {
+ int *arr[] = {p, q};
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"arr", 2U}, {"p", 1U}},
+ {{"arr", 2U}, {"q", 1U}},
+ }));
+}
+
+TEST_F(PointerAssignmentsTest, StructInitList) {
+ auto Sum = setUpTest(R"cpp(
+ struct S { int *a; int *b; };
+ void foo(int *p, int *q) {
+ S s = {p, q};
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"a", 1U}, {"p", 1U}},
+ {{"b", 1U}, {"q", 1U}},
+ }));
+}
+
+// A union initialized with a brace-enclosed initializer:
+TEST_F(PointerAssignmentsTest, UnionInitList) {
+ auto Sum = setUpTest(R"cpp(
+ union U { int *x; int y; };
+ void foo(int *p) {
+ U u = {p};
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"x", 1U}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, NestedInitList) {
+ auto Sum = setUpTest(R"cpp(
+ struct Inner { int * a; int * b; };
+ struct S { Inner c; int * d; };
+ void foo(int *p, int *q, int *r) {
+ S s = {{p, q}, r};
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"a", 1U}, {"p", 1U}},
+ {{"b", 1U}, {"q", 1U}},
+ {{"d", 1U}, {"r", 1U}},
+ }));
+}
+
+TEST_F(PointerAssignmentsTest, NestedInitList2) {
+ auto Sum = setUpTest(R"cpp(
+ union Inner { int * a; int b; };
+ struct S { Inner c; int * d; };
+ void foo(int *p, int *q) {
+ S s = {{p}, q};
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"a", 1U}, {"p", 1U}},
+ {{"d", 1U}, {"q", 1U}},
+ }));
+}
+
+TEST_F(PointerAssignmentsTest, NestedInitList3) {
+ auto Sum = setUpTest(R"cpp(
+ struct Inner { int * a; int * b; };
+ union S { Inner c; int * d; };
+ void foo(int *p, int *q) {
+ S s = {{p, q}};
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"a", 1U}, {"p", 1U}},
+ {{"b", 1U}, {"q", 1U}},
+ }));
+}
+
+TEST_F(PointerAssignmentsTest, NestedArrayInitList) {
+ auto Sum = setUpTest(R"cpp(
+ void foo(int *p, int *q, int *r, int *s) {
+ int *arr[][2] = {{p, q}, {r, s}};
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"arr", 3U}, {"p", 1U}},
+ {{"arr", 3U}, {"q", 1U}},
+ {{"arr", 3U}, {"r", 1U}},
+ {{"arr", 3U}, {"s", 1U}},
+ }));
+}
+
+TEST_F(PointerAssignmentsTest, MixedNestedArrayStructInitList) {
+ auto Sum = setUpTest(R"cpp(
+ struct T { int *arr[2]; };
+ void foo(int *p, int *q, int *r, int *s) {
+ T t[2] = {{p, q}, {r, s}};
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"arr", 2U}, {"p", 1U}},
+ {{"arr", 2U}, {"q", 1U}},
+ {{"arr", 2U}, {"r", 1U}},
+ {{"arr", 2U}, {"s", 1U}},
+ }));
+}
+
+TEST_F(PointerAssignmentsTest, ArrayOfStructInitList) {
+ auto Sum = setUpTest(R"cpp(
+ struct S { int *a; int *b; };
+ void foo(int *p, int *q, int *r, int *s) {
+ S arr[] = {{p, q}, {r, s}};
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"a", 1U}, {"p", 1U}},
+ {{"b", 1U}, {"q", 1U}},
+ {{"a", 1U}, {"r", 1U}},
+ {{"b", 1U}, {"s", 1U}},
+ }));
+}
+
+//////////////////////////////////////////////////////////////
+// Return Tests. //
+//////////////////////////////////////////////////////////////
+
+TEST_F(PointerAssignmentsTest, ReturnEdge) {
+ auto Sum = setUpTest(R"cpp(
+ int *foo(int *p) {
+ return p;
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"foo", 1U, true}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, MultipleReturnEdges) {
+ auto Sum = setUpTest(R"cpp(
+ int *foo(int *p, int *q, bool cond) {
+ if (cond)
+ return p;
+ return q;
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {
+ {{"foo", 1U, true}, {"p", 1U}},
+ {{"foo", 1U, true}, {"q", 1U}},
+ }));
+}
+
+TEST_F(PointerAssignmentsTest, NoReturnEdgeForNonPointerReturnType) {
+ auto Sum = setUpTest(R"cpp(
+ int foo(int *p, int x) {
+ return x;
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {}));
+}
+
+TEST_F(PointerAssignmentsTest, ReturnEdgeNotFromNestedFunction) {
+ auto *Code = R"cpp(
+ int *foo(int *p) {
+ struct Inner {
+ int *bar(int *q) { return q; }
+ };
+ return p;
+ }
+ )cpp";
+ auto Sum = setUpTest(Code, "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"foo", 1U, true}, {"p", 1U}}}));
+
+ Sum = setUpTest(Code, "bar");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"bar", 1U, true}, {"q", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, ReturnEdgeInClassMethod) {
+ auto Sum = setUpTest(R"cpp(
+ void foo() {
+ struct S {
+ int *method(int *p, int *q) { return p; }
+ };
+ }
+ )cpp",
+ "method");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"method", 1U, true}, {"p", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, NoEdgeFromIndirectCall) {
+ auto Sum = setUpTest(R"cpp(
+ void bar(int *param1);
+ void baz(int *param2);
+
+ void foo(int *p, void (*fp)(int *)) {
+ fp(p);
+ }
+
+ void main() {
+ int *q;
+ foo(q, bar);
+ foo(q, baz);
+ }
+ )cpp",
+ "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(
+ *Sum,
+ makeEdges(
+ __LINE__,
+ /* FIXME or TBD: Currently indirect calls produce no edge: */ {}));
+}
+
+//////////////////////////////////////////////////////////////
+// Lambda Tests. //
+//////////////////////////////////////////////////////////////
+
+TEST_F(PointerAssignmentsTest, ReturnInLambda) {
+ StringRef Code = R"cpp(
+ int* foo(int *p) {
+ auto local = [](int *r) { return r; };
+ return local(p);
+ }
+ )cpp";
+
+ auto Sum = setUpTest(Code, "foo");
+
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"r", 1U}, {"p", 1U}},
+ {{"foo", 1U, true},
+ {LambdaOfVar{"local"}, 1U, true}}}));
+
+ Sum = setUpTest(Code, LambdaOfVar{"local"});
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__,
+ {{{LambdaOfVar{"local"}, 1U, true}, {"r", 1U}}}));
+}
+
+TEST_F(PointerAssignmentsTest, NestedLambdaAssign) {
+ StringRef Code = R"cpp(
+ void foo() {
+ auto outer_lambda = [](int *r, int *s) {
+ s = r;
+ auto inner_lambda = [](int *x, int *y) { y = x; };
+ };
+ }
+ )cpp";
+
+ auto Sum = setUpTest(Code, LambdaOfVar{"outer_lambda"});
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"s", 1U}, {"r", 1U}}}));
+
+ Sum = setUpTest(Code, LambdaOfVar{"inner_lambda"});
+ ASSERT_NE(Sum, nullptr);
+ EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"y", 1U}, {"x", 1U}}}));
+}
+} // namespace
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
index 5da36d071be32..e8c8918c7732a 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -1,5 +1,6 @@
add_distinct_clang_unittest(ClangScalableAnalysisTests
Analyses/UnsafeBufferUsageTest.cpp
+ Analyses/PointerAssignmentsTest.cpp
ASTEntityMappingTest.cpp
BuildNamespaceTest.cpp
EntityIdTableTest.cpp
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp
index 28f4d089b5764..e6e21d3610c7f 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Registries/SummaryExtractorRegistryTest.cpp
@@ -42,6 +42,7 @@ TEST(SummaryExtractorRegistryTest, EnumeratingRegistryEntries) {
"MockSummaryExtractor1",
"MockSummaryExtractor2",
"NoOpExtractor",
+ "PointerAssignmentsTUSummaryExtractor",
"UnsafeBufferUsageTUSummaryExtractor",
}));
}
>From e5dbc4890c105fce5ba81bd7d78a23ba5e82ecd7 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 25 Mar 2026 18:42:26 -0700
Subject: [PATCH 15/51] address some comments
---
.../UnsafeBufferUsage/UnsafeBufferUsage.h | 2 +-
.../UnsafeBufferUsage/UnsafeBufferUsage.cpp | 23 +++++++++----------
2 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 2b36c47fe67a5..38077173efb6a 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -38,8 +38,8 @@ class EntityPointerLevel {
EntityId Entity;
unsigned PointerLevel;
- friend class UnsafeBufferUsageTUSummaryExtractor;
friend class UnsafeBufferUsageEntitySummary;
+ friend class UnsafeBufferUsageTUSummaryExtractor;
EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
: Entity(Entity), PointerLevel(PointerLevel) {}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 44eaa20c74236..59b3a1c34ff52 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -16,10 +16,9 @@ constexpr const char *const UnsafeBuffersKey = "UnsafeBuffers";
namespace clang::ssaf {
using Object = llvm::json::Object;
using Array = llvm::json::Array;
-using Value = llvm::json::Value;
llvm::json::Object UnsafeBufferUsageEntitySummary::jsonSerializeFn(
- const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+ const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityIdToJSON) {
// Writes a EntityPointerLevel as
// Array {
// Object {
@@ -31,15 +30,14 @@ llvm::json::Object UnsafeBufferUsageEntitySummary::jsonSerializeFn(
for (const auto &EPL :
static_cast<const UnsafeBufferUsageEntitySummary &>(ES).UnsafeBuffers)
- UnsafeBuffersData.push_back(
- Value(Array{// EntityId:
- Value(EntityId2JSON(EPL.getEntity())),
- // PointerLevel:
- Value(EPL.getPointerLevel())}));
+ UnsafeBuffersData.push_back(Array{// EntityId:
+ EntityIdToJSON(EPL.getEntity()),
+ // PointerLevel:
+ EPL.getPointerLevel()});
Object Data;
- Data[UnsafeBuffersKey] = Value(std::move(UnsafeBuffersData));
+ Data[UnsafeBuffersKey] = std::move(UnsafeBuffersData);
return Data;
}
@@ -48,12 +46,13 @@ UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
const llvm::json::Object &Data, EntityIdTable &,
JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
const Array *UnsafeBuffersData = Data.getArray(UnsafeBuffersKey);
- constexpr const char *const ErrMsg = "unrecognized UnsafeBufferUsageEntitySummary data";
+ constexpr const char *const ErrMsg =
+ "unrecognized UnsafeBufferUsageEntitySummary data";
if (!UnsafeBuffersData)
return llvm::createStringError(ErrMsg);
- EntityPointerLevelSet UnsafeBuffers;
+ EntityPointerLevelSet EPLs;
for (auto &EltData : *UnsafeBuffersData) {
const Array *EltDataAsArr = EltData.getAsArray();
@@ -71,10 +70,10 @@ UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
if (!Id)
return Id.takeError();
- UnsafeBuffers.insert(EntityPointerLevel(Id.get(), *PtrLvData));
+ EPLs.insert(EntityPointerLevel(Id.get(), *PtrLvData));
}
return std::make_unique<UnsafeBufferUsageEntitySummary>(
- UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers)));
+ UnsafeBufferUsageEntitySummary(std::move(EPLs)));
}
static llvm::Registry<JSONFormat::FormatInfo>::Add<
>From 2cbbc1af59d00069202fc3051a01b2913c6e63d0 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 26 Mar 2026 16:45:57 -0700
Subject: [PATCH 16/51] address comments
---
.../UnsafeBufferUsage/UnsafeBufferUsage.cpp | 10 ++--
.../tu-summary-bad-element.json | 53 +++++++++++++++++
.../tu-summary-bad-ptr-level.json | 58 +++++++++++++++++++
.../UnsafeBufferUsage/tu-summary-no-key.json | 58 +++++++++++++++++++
.../tu-summary-serialization.test | 16 ++++-
.../Analysis/Scalable/ssaf-format/list.test | 4 +-
.../UnsafeBufferUsageTest.cpp | 7 +--
7 files changed, 194 insertions(+), 12 deletions(-)
create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-element.json
create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-ptr-level.json
create mode 100644 clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-no-key.json
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 59b3a1c34ff52..d2257816c9ad9 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -8,6 +8,7 @@
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
+#include "llvm/Support/Error.h"
namespace {
constexpr const char *const UnsafeBuffersKey = "UnsafeBuffers";
@@ -46,11 +47,10 @@ UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
const llvm::json::Object &Data, EntityIdTable &,
JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
const Array *UnsafeBuffersData = Data.getArray(UnsafeBuffersKey);
- constexpr const char *const ErrMsg =
- "unrecognized UnsafeBufferUsageEntitySummary data";
if (!UnsafeBuffersData)
- return llvm::createStringError(ErrMsg);
+ return llvm::createStringError("expected a json::Object with a key %s",
+ UnsafeBuffersKey);
EntityPointerLevelSet EPLs;
@@ -58,13 +58,13 @@ UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
const Array *EltDataAsArr = EltData.getAsArray();
if (!EltDataAsArr || EltDataAsArr->size() != 2)
- return llvm::createStringError(ErrMsg);
+ return llvm::createStringError("expected a json::Array of size 2");
const Object *IdData = (*EltDataAsArr)[0].getAsObject();
std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
if (!IdData || !PtrLvData)
- return llvm::createStringError(ErrMsg);
+ return llvm::createStringError("expected a json::Value of integer type");
llvm::Expected<EntityId> Id = EntityIdFromJSON(*IdData);
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-element.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-element.json
new file mode 100644
index 0000000000000..10d8c9457aaeb
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-element.json
@@ -0,0 +1,53 @@
+{
+ "data": [
+ {
+ "summary_data": [
+ {
+ "entity_id": 2,
+ "entity_summary": {
+ "UnsafeBuffers": [
+ 42
+ ]
+ }
+ }
+ ],
+ "summary_name": "UnsafeBufferUsage"
+ }
+ ],
+ "id_table": [
+ {
+ "id": 2,
+ "name": {
+ "namespace": [],
+ "suffix": "",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ },
+ {
+ "id": 0,
+ "name": {
+ "namespace": [],
+ "suffix": "1",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ },
+ {
+ "id": 2,
+ "linkage": {
+ "type": "Internal"
+ }
+ }
+ ],
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "Mock.cpp"
+ }
+}
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-ptr-level.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-ptr-level.json
new file mode 100644
index 0000000000000..6b59ff39e6913
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-ptr-level.json
@@ -0,0 +1,58 @@
+{
+ "data": [
+ {
+ "summary_data": [
+ {
+ "entity_id": 2,
+ "entity_summary": {
+ "UnsafeBuffers": [
+ [
+ {
+ "@": 0
+ },
+ "not-an-integer"
+ ]
+ ]
+ }
+ }
+ ],
+ "summary_name": "UnsafeBufferUsage"
+ }
+ ],
+ "id_table": [
+ {
+ "id": 2,
+ "name": {
+ "namespace": [],
+ "suffix": "",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ },
+ {
+ "id": 0,
+ "name": {
+ "namespace": [],
+ "suffix": "1",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ },
+ {
+ "id": 2,
+ "linkage": {
+ "type": "Internal"
+ }
+ }
+ ],
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "Mock.cpp"
+ }
+}
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-no-key.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-no-key.json
new file mode 100644
index 0000000000000..555070ce3e660
--- /dev/null
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-no-key.json
@@ -0,0 +1,58 @@
+{
+ "data": [
+ {
+ "summary_data": [
+ {
+ "entity_id": 2,
+ "entity_summary": {
+ "NotUnsafeBuffers": [
+ [
+ {
+ "@": 0
+ },
+ 1
+ ]
+ ]
+ }
+ }
+ ],
+ "summary_name": "UnsafeBufferUsage"
+ }
+ ],
+ "id_table": [
+ {
+ "id": 2,
+ "name": {
+ "namespace": [],
+ "suffix": "",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ },
+ {
+ "id": 0,
+ "name": {
+ "namespace": [],
+ "suffix": "1",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ },
+ {
+ "id": 2,
+ "linkage": {
+ "type": "Internal"
+ }
+ }
+ ],
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "Mock.cpp"
+ }
+}
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
index e8cf6020df16f..3853dc2c2408a 100644
--- a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
@@ -1,4 +1,18 @@
// RUN: rm -f %t.json
// RUN: clang-ssaf-format -type=tu %S/tu-summary.json -o %t.json
// RUN: diff %S/tu-summary.json %t.json
-//
+
+// Negative tests:
+
+// RUN: not clang-ssaf-format -type=tu %S/tu-summary-no-key.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-NO-KEY
+// CHECK-NO-KEY: expected a json::Object with a key UnsafeBuffers
+
+
+// RUN: not clang-ssaf-format -type=tu %S/tu-summary-bad-element.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-BAD-ELEMENT
+// CHECK-BAD-ELEMENT: expected a json::Array of size 2
+
+// RUN: not clang-ssaf-format -type=tu %S/tu-summary-bad-ptr-level.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-BAD-PTR-LEVEL
+// CHECK-BAD-PTR-LEVEL: expected a json::Value of integer type
diff --git a/clang/test/Analysis/Scalable/ssaf-format/list.test b/clang/test/Analysis/Scalable/ssaf-format/list.test
index c89dc7c60e62a..28588a18230f9 100644
--- a/clang/test/Analysis/Scalable/ssaf-format/list.test
+++ b/clang/test/Analysis/Scalable/ssaf-format/list.test
@@ -5,6 +5,6 @@
// CHECK: Registered serialization formats:
// CHECK-EMPTY:
-// CHECK-NEXT: 1. json - JSON serialization format
+// CHECK-NEXT: 1. json - JSON serialization format
// CHECK-NEXT: Analyses:
-// CHECK-NEXT: 1.1. UnsafeBufferUsage JSON Format info for UnsafeBufferUsageEntitySummary
+// CHECK-NEXT: 1.1. UnsafeBufferUsage - JSON Format info for UnsafeBufferUsageEntitySummary
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 8b5b76b6e58b6..d40ff295ea940 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -259,13 +259,12 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
{"q", 4U}}));
using Object = llvm::json::Object;
- using Value = llvm::json::Value;
+
std::map<EntityId, uint64_t> DummyTable{{*getEntityId("p"), 42},
{*getEntityId("q"), 108}};
Object JData = UnsafeBufferUsageEntitySummary::jsonSerializeFn(
- *Sum, [&DummyTable](EntityId Id) {
- return Object{{"@", Value(DummyTable[Id])}};
- });
+ *Sum,
+ [&DummyTable](EntityId Id) { return Object{{"@", DummyTable[Id]}}; });
EXPECT_EQ(llvm::formatv("{0:2}", llvm::json::Value(std::move(JData))).str(),
SerilizationTestOracle);
>From 84206125a0eca09c76d9b3783d8a920933c7e735 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Fri, 27 Mar 2026 12:07:19 -0700
Subject: [PATCH 17/51] Update
clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
adjust the position of the file title
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index d2257816c9ad9..2ec0614f0cefc 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -1,4 +1,4 @@
-//===---------- UnsafeBufferUsage.cpp -------------------------------------===//
+//===- UnsafeBufferUsage.cpp ----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
>From f8a2e0e6668d9981b4ab7d65ee8d958cdc89a26f Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Fri, 27 Mar 2026 12:10:30 -0700
Subject: [PATCH 18/51] Update
clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Remove "#include SSAFForceLinker.h"
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 2ec0614f0cefc..a20b5895d55c7 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
#include "llvm/Support/Error.h"
namespace {
>From 354f742f5e173c8a62c542a0ea498f33576bcdac Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Fri, 27 Mar 2026 12:14:26 -0700
Subject: [PATCH 19/51] Update
clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Replace "const char * const" with "llvm::StringLiteral"
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index a20b5895d55c7..27bfde4f2829e 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -9,9 +9,7 @@
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
#include "llvm/Support/Error.h"
-namespace {
-constexpr const char *const UnsafeBuffersKey = "UnsafeBuffers";
-} // namespace
+static constexpr llvm::StringLiteral UnsafeBuffersKey = "UnsafeBuffers";
namespace clang::ssaf {
using Object = llvm::json::Object;
>From 157f3c29f5a77e1d4695b590c450968f3eeedcb9 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Sun, 29 Mar 2026 15:53:41 -0700
Subject: [PATCH 20/51] refactor UnsafeBufferUsage serialization API and test
for Format independency
---
.../UnsafeBufferUsage/UnsafeBufferUsage.h | 74 ++++++++--
.../UnsafeBufferUsage/UnsafeBufferUsage.cpp | 97 +++++--------
.../{ => Inputs}/tu-summary-bad-element.json | 0
.../tu-summary-bad-ptr-level.json | 0
.../{ => Inputs}/tu-summary-no-key.json | 0
.../{ => Inputs}/tu-summary.json | 0
.../tu-summary-serialization.test | 12 +-
.../Analysis/Scalable/ssaf-format/list.test | 2 +-
.../Analyses/MockSerialization.h | 96 +++++++++++++
.../UnsafeBufferUsageTest.cpp | 127 +++++++-----------
10 files changed, 245 insertions(+), 163 deletions(-)
rename clang/test/Analysis/Scalable/UnsafeBufferUsage/{ => Inputs}/tu-summary-bad-element.json (100%)
rename clang/test/Analysis/Scalable/UnsafeBufferUsage/{ => Inputs}/tu-summary-bad-ptr-level.json (100%)
rename clang/test/Analysis/Scalable/UnsafeBufferUsage/{ => Inputs}/tu-summary-no-key.json (100%)
rename clang/test/Analysis/Scalable/UnsafeBufferUsage/{ => Inputs}/tu-summary.json (100%)
create mode 100644 clang/unittests/ScalableStaticAnalysisFramework/Analyses/MockSerialization.h
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 38077173efb6a..faf48b55bfaaf 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -13,6 +13,7 @@
#include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
+#include "llvm/ADT/StringRef.h"
#include <set>
namespace clang::ssaf {
@@ -92,6 +93,9 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
UnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers)
: EntitySummary(), UnsafeBuffers(std::move(UnsafeBuffers)) {}
+ static constexpr llvm::StringLiteral SummarySerializationKey =
+ "UnsafeBuffers";
+
public:
SummaryName getSummaryName() const override { return summaryName(); };
@@ -105,23 +109,65 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
bool empty() const { return UnsafeBuffers.empty(); }
- static llvm::json::Object
- jsonSerializeFn(const EntitySummary &ES,
- JSONFormat::EntityIdToJSONFn EntityId2JSON);
+ static SummaryName summaryName() { return SummaryName{"UnsafeBufferUsage"}; }
- static llvm::Expected<std::unique_ptr<EntitySummary>>
- jsonDeserializeFn(const llvm::json::Object &Data, EntityIdTable &,
- JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
+ // A SerializationAPI implementation for a specific Format needs to implement:
+ //
+ // 1) Abstract data types, Object (map), Array, Value, ..., etc. with
+ // supported operations, and
+ // 2) EntityIdToFormatFn, and
+ // 3) EntityIdFromFotmatFn.
+
+ // Format-independent (de-)serialization functions:
+ template <typename SerializationAPI>
+ typename SerializationAPI::Object static serialize(
+ SerializationAPI &SA, const UnsafeBufferUsageEntitySummary &S) {
+ using Array = typename SerializationAPI::Array;
+ using Object = typename SerializationAPI::Object;
+ Array UnsafeBuffersData;
+
+ UnsafeBuffersData.reserve(S.UnsafeBuffers.size());
+ for (const auto &EPL : S.UnsafeBuffers)
+ UnsafeBuffersData.push_back(
+ Array{SA.EntityIdToFormat(EPL.getEntity()), EPL.getPointerLevel()});
+ return Object{{SummarySerializationKey.data(), std::move(UnsafeBuffersData)}};
+ }
- static SummaryName summaryName() { return SummaryName{"UnsafeBufferUsage"}; }
-};
+ template <typename SerializationAPI>
+ llvm::Expected<std::unique_ptr<EntitySummary>> static deserialize(
+ SerializationAPI &SA, const typename SerializationAPI::Object &Data) {
+ using Array = typename SerializationAPI::Array;
+ using Object = typename SerializationAPI::Object;
+ const Array *UnsafeBuffersData = Data.getArray(SummarySerializationKey.data());
+
+ if (!UnsafeBuffersData)
+ return llvm::createStringError("expected a json::Object with a key %s",
+ SummarySerializationKey.data());
+
+ EntityPointerLevelSet EPLs;
-struct UnsafeBufferUsageJSONFormatInfo : JSONFormat::FormatInfo {
- UnsafeBufferUsageJSONFormatInfo()
- : JSONFormat::FormatInfo(
- UnsafeBufferUsageEntitySummary::summaryName(),
- UnsafeBufferUsageEntitySummary::jsonSerializeFn,
- UnsafeBufferUsageEntitySummary::jsonDeserializeFn) {}
+ for (auto &EltData : *UnsafeBuffersData) {
+ const Array *EltDataAsArr = EltData.getAsArray();
+
+ if (!EltDataAsArr || EltDataAsArr->size() != 2)
+ return llvm::createStringError("expected a json::Array of size 2");
+
+ const Object *IdData = (*EltDataAsArr)[0].getAsObject();
+ std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
+
+ if (!IdData || !PtrLvData)
+ return llvm::createStringError(
+ "expected a json::Value of integer type");
+
+ llvm::Expected<EntityId> Id = SA.EntityIdFromFormat(*IdData);
+
+ if (!Id)
+ return Id.takeError();
+ EPLs.insert(EntityPointerLevel(Id.get(), *PtrLvData));
+ }
+ return std::make_unique<UnsafeBufferUsageEntitySummary>(
+ UnsafeBufferUsageEntitySummary(std::move(EPLs)));
+ }
};
} // namespace clang::ssaf
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 27bfde4f2829e..41b7c250773bf 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -7,79 +7,52 @@
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+#include "clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h" // IWYU pragma: keep
#include "llvm/Support/Error.h"
-static constexpr llvm::StringLiteral UnsafeBuffersKey = "UnsafeBuffers";
-
-namespace clang::ssaf {
-using Object = llvm::json::Object;
-using Array = llvm::json::Array;
-
-llvm::json::Object UnsafeBufferUsageEntitySummary::jsonSerializeFn(
- const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityIdToJSON) {
- // Writes a EntityPointerLevel as
- // Array {
- // Object {
- // "@" : [entity-id]
- // },
- // [pointer-level]
- // }
- Array UnsafeBuffersData;
-
- for (const auto &EPL :
- static_cast<const UnsafeBufferUsageEntitySummary &>(ES).UnsafeBuffers)
- UnsafeBuffersData.push_back(Array{// EntityId:
- EntityIdToJSON(EPL.getEntity()),
- // PointerLevel:
- EPL.getPointerLevel()});
-
- Object Data;
-
- Data[UnsafeBuffersKey] = std::move(UnsafeBuffersData);
- return Data;
+using namespace clang;
+using namespace ssaf;
+
+namespace {
+struct JSONSerializationAPI {
+ using Object = llvm::json::Object;
+ using Array = llvm::json::Array;
+ using Value = llvm::json::Value;
+
+ JSONFormat::EntityIdToJSONFn EntityIdToFormat;
+ JSONFormat::EntityIdFromJSONFn EntityIdFromFormat;
+};
+} // namespace
+
+// Adapter functions to the current API:
+static llvm::json::Object jsonSerializeFn(const EntitySummary &Sum,
+ JSONFormat::EntityIdToJSONFn Fn) {
+ JSONSerializationAPI SA{Fn, {}};
+ return UnsafeBufferUsageEntitySummary::serialize(
+ SA, *static_cast<const UnsafeBufferUsageEntitySummary *>(&Sum));
}
-llvm::Expected<std::unique_ptr<EntitySummary>>
-UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
- const llvm::json::Object &Data, EntityIdTable &,
- JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
- const Array *UnsafeBuffersData = Data.getArray(UnsafeBuffersKey);
-
- if (!UnsafeBuffersData)
- return llvm::createStringError("expected a json::Object with a key %s",
- UnsafeBuffersKey);
-
- EntityPointerLevelSet EPLs;
-
- for (auto &EltData : *UnsafeBuffersData) {
- const Array *EltDataAsArr = EltData.getAsArray();
-
- if (!EltDataAsArr || EltDataAsArr->size() != 2)
- return llvm::createStringError("expected a json::Array of size 2");
-
- const Object *IdData = (*EltDataAsArr)[0].getAsObject();
- std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
-
- if (!IdData || !PtrLvData)
- return llvm::createStringError("expected a json::Value of integer type");
-
- llvm::Expected<EntityId> Id = EntityIdFromJSON(*IdData);
-
- if (!Id)
- return Id.takeError();
- EPLs.insert(EntityPointerLevel(Id.get(), *PtrLvData));
- }
- return std::make_unique<UnsafeBufferUsageEntitySummary>(
- UnsafeBufferUsageEntitySummary(std::move(EPLs)));
+static llvm::Expected<std::unique_ptr<EntitySummary>>
+jsonDeserializeFn(const llvm::json::Object &Data, EntityIdTable &,
+ JSONFormat::EntityIdFromJSONFn Fn) {
+ JSONSerializationAPI SA{{}, Fn};
+ return UnsafeBufferUsageEntitySummary::deserialize<JSONSerializationAPI>(
+ SA, Data);
}
+struct UnsafeBufferUsageJSONFormatInfo : JSONFormat::FormatInfo {
+ UnsafeBufferUsageJSONFormatInfo()
+ : JSONFormat::FormatInfo(UnsafeBufferUsageEntitySummary::summaryName(),
+ jsonSerializeFn, jsonDeserializeFn) {}
+};
+
static llvm::Registry<JSONFormat::FormatInfo>::Add<
UnsafeBufferUsageJSONFormatInfo>
RegisterUnsafeBufferUsageJSONFormatInfo(
"UnsafeBufferUsage",
"JSON Format info for UnsafeBufferUsageEntitySummary");
-} // namespace clang::ssaf
-
// NOLINTNEXTLINE(misc-use-internal-linkage)
volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource = 0;
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-element.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary-bad-element.json
similarity index 100%
rename from clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-element.json
rename to clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary-bad-element.json
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-ptr-level.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary-bad-ptr-level.json
similarity index 100%
rename from clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-bad-ptr-level.json
rename to clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary-bad-ptr-level.json
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-no-key.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary-no-key.json
similarity index 100%
rename from clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-no-key.json
rename to clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary-no-key.json
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary.json
similarity index 100%
rename from clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary.json
rename to clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary.json
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
index 3853dc2c2408a..6a12949f3bbb4 100644
--- a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
@@ -1,18 +1,18 @@
-// RUN: rm -f %t.json
-// RUN: clang-ssaf-format -type=tu %S/tu-summary.json -o %t.json
-// RUN: diff %S/tu-summary.json %t.json
+// RUN: rm -rf %t.json
+// RUN: clang-ssaf-format -type=tu %S/Inputs/tu-summary.json -o %t.json
+// RUN: diff %S/Inputs/tu-summary.json %t.json
// Negative tests:
-// RUN: not clang-ssaf-format -type=tu %S/tu-summary-no-key.json 2>&1 \
+// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-no-key.json 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-NO-KEY
// CHECK-NO-KEY: expected a json::Object with a key UnsafeBuffers
-// RUN: not clang-ssaf-format -type=tu %S/tu-summary-bad-element.json 2>&1 \
+// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-element.json 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-BAD-ELEMENT
// CHECK-BAD-ELEMENT: expected a json::Array of size 2
-// RUN: not clang-ssaf-format -type=tu %S/tu-summary-bad-ptr-level.json 2>&1 \
+// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-ptr-level.json 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-BAD-PTR-LEVEL
// CHECK-BAD-PTR-LEVEL: expected a json::Value of integer type
diff --git a/clang/test/Analysis/Scalable/ssaf-format/list.test b/clang/test/Analysis/Scalable/ssaf-format/list.test
index 28588a18230f9..766d6eba1821b 100644
--- a/clang/test/Analysis/Scalable/ssaf-format/list.test
+++ b/clang/test/Analysis/Scalable/ssaf-format/list.test
@@ -7,4 +7,4 @@
// CHECK-EMPTY:
// CHECK-NEXT: 1. json - JSON serialization format
// CHECK-NEXT: Analyses:
-// CHECK-NEXT: 1.1. UnsafeBufferUsage - JSON Format info for UnsafeBufferUsageEntitySummary
+// CHECK-DAG: 1.1. UnsafeBufferUsage - JSON Format info for UnsafeBufferUsageEntitySummary
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/MockSerialization.h b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/MockSerialization.h
new file mode 100644
index 0000000000000..dfe61f20bda71
--- /dev/null
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/MockSerialization.h
@@ -0,0 +1,96 @@
+//===- MockSerialization.h ------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// A mock implementation of SerializationAPI for format-independent testing of
+// summary serialization
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_MOCKSERIALIZATION_H
+#define LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_MOCKSERIALIZATION_H
+
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+#include <initializer_list>
+#include <map>
+#include <optional>
+#include <variant>
+#include <vector>
+
+using namespace llvm;
+using namespace clang;
+using namespace ssaf;
+
+struct MockSerializationAPI {
+ struct Value;
+ using Array = std::vector<Value>;
+
+ struct Object : public std::map<StringRef, Value> {
+ Object(std::initializer_list<Object::value_type> IL)
+ : std::map<StringRef, Value>(std::move(IL)) {};
+
+ const Array *getArray(StringRef Key) const {
+ auto I = find(Key);
+ if (I == end())
+ return nullptr;
+ return I->second.getAsArray();
+ }
+ };
+
+ struct Value {
+ private:
+ std::variant<Object, Array, uint64_t> Data;
+
+ public:
+ Value(Object Data) : Data(Data) {}
+ Value(Array Data) : Data(Data) {}
+ Value(uint64_t Data) : Data(Data) {}
+
+ const Array *getAsArray() const { return std::get_if<Array>(&Data); };
+ const Object *getAsObject() const { return std::get_if<Object>(&Data); };
+ std::optional<uint64_t> getAsInteger() const {
+ if (auto *Int = std::get_if<uint64_t>(&Data))
+ return *Int;
+ return std::nullopt;
+ };
+
+ bool operator==(const Value &Other) const {
+ if (std::holds_alternative<Object>(Data))
+ return Other.getAsObject() &&
+ std::equal_to<std::map<StringRef, Value>>()(
+ *getAsObject(), *Other.getAsObject());
+ if (std::holds_alternative<Array>(Data))
+ return Other.getAsArray() && *getAsArray() == *Other.getAsArray();
+ if (std::holds_alternative<uint64_t>(Data))
+ return Other.getAsInteger() && *getAsInteger() == *Other.getAsInteger();
+ return false;
+ }
+ };
+
+ Object EntityIdToFormat(EntityId Id) {
+ auto Iter = llvm::find(IDs, Id);
+
+ if (Iter == IDs.end())
+ Iter = IDs.insert(IDs.end(), Id);
+ return Object{{"@", uint64_t(Iter - IDs.begin())}};
+ }
+
+ llvm::Expected<EntityId> EntityIdFromFormat(const Object &Data) {
+ if (Data.count("@"))
+ if (auto Idx = Data.at("@").getAsInteger())
+ return IDs[*Idx];
+ return llvm::createStringError("failed to convert Data to EntityId");
+ }
+
+private:
+ std::vector<EntityId> IDs;
+};
+#endif // LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_MOCKSERIALIZATION_H
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index d40ff295ea940..fa12f7e924bc3 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -7,19 +7,22 @@
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "Analyses/MockSerialization.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Error.h"
#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include <initializer_list>
using namespace clang;
using namespace ssaf;
@@ -187,59 +190,32 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
}
//////////////////////////////////////////////////////////////
-// JSON Tests //
+// (De-)Serialization Tests //
//////////////////////////////////////////////////////////////
-// Oracle JSON output for the example:
-// void foo(int ***p, int ****q, int x) {
-// p[5][5][5];
-// q[5][5][5][5];
-// }
-constexpr const char *const SerilizationTestOracle = R"cpp({
- "UnsafeBuffers": [
- [
- {
- "@": 42
- },
- 1
- ],
- [
- {
- "@": 42
- },
- 2
- ],
- [
- {
- "@": 42
- },
- 3
- ],
- [
- {
- "@": 108
- },
- 1
- ],
- [
- {
- "@": 108
- },
- 2
- ],
- [
- {
- "@": 108
- },
- 3
- ],
- [
- {
- "@": 108
- },
- 4
- ]
- ]
-})cpp";
+
+static MockSerializationAPI::Object SerilizedOracle(EntityId P, EntityId Q,
+ MockSerializationAPI &MSA) {
+ // Oracle serialization data for the example:
+ // void foo(int ***p, int ****q, int x) {
+ // p[5][5][5];
+ // q[5][5][5][5];
+ // }
+ using Array = MockSerializationAPI::Array;
+ using Object = MockSerializationAPI::Object;
+
+ Object PData = MSA.EntityIdToFormat(P);
+ Object QData = MSA.EntityIdToFormat(Q);
+
+ return Object{std::pair{"UnsafeBuffers", Array{
+ Array{PData, 1U},
+ Array{PData, 2U},
+ Array{PData, 3U},
+ Array{QData, 1U},
+ Array{QData, 2U},
+ Array{QData, 3U},
+ Array{QData, 4U},
+ }}};
+}
TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
auto Sum = setUpTest(R"cpp(
@@ -258,16 +234,15 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
{"q", 3U},
{"q", 4U}}));
- using Object = llvm::json::Object;
+ MockSerializationAPI MSA;
+ MockSerializationAPI::Object JData =
+ UnsafeBufferUsageEntitySummary::serialize<MockSerializationAPI>(MSA,
+ *Sum);
- std::map<EntityId, uint64_t> DummyTable{{*getEntityId("p"), 42},
- {*getEntityId("q"), 108}};
- Object JData = UnsafeBufferUsageEntitySummary::jsonSerializeFn(
- *Sum,
- [&DummyTable](EntityId Id) { return Object{{"@", DummyTable[Id]}}; });
+ auto EntityP = getEntityId("p");
+ auto EntityQ = getEntityId("q");
- EXPECT_EQ(llvm::formatv("{0:2}", llvm::json::Value(std::move(JData))).str(),
- SerilizationTestOracle);
+ EXPECT_EQ(JData, SerilizedOracle(*EntityP, *EntityQ, MSA));
}
TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageDeserializeTest) {
@@ -287,25 +262,17 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageDeserializeTest) {
{"q", 3U},
{"q", 4U}}));
- using Object = llvm::json::Object;
- using Value = llvm::json::Value;
- std::map<uint64_t, EntityId> DummyTable{{42, *getEntityId("p")},
- {108, *getEntityId("q")}};
- Expected<Value> ParsedJSON = llvm::json::parse(SerilizationTestOracle);
-
- ASSERT_THAT_EXPECTED(ParsedJSON, llvm::Succeeded());
- ASSERT_NE(ParsedJSON->getAsObject(), nullptr);
-
- EntityIdTable Ignored;
- auto ParsedSum = UnsafeBufferUsageEntitySummary::jsonDeserializeFn(
- *ParsedJSON->getAsObject(), Ignored,
- [&DummyTable](const Object &O) -> Expected<EntityId> {
- return DummyTable.at(O.getInteger("@").value());
- });
-
- ASSERT_THAT_EXPECTED(ParsedSum, llvm::Succeeded());
- EXPECT_EQ(*static_cast<UnsafeBufferUsageEntitySummary *>(ParsedSum->get()),
- *Sum);
+ MockSerializationAPI MSA;
+ auto EntityP = getEntityId("p");
+ auto EntityQ = getEntityId("q");
+ auto DeserializedSum =
+ UnsafeBufferUsageEntitySummary::deserialize<MockSerializationAPI>(
+ MSA, SerilizedOracle(*EntityP, *EntityQ, MSA));
+
+ ASSERT_THAT_ERROR(DeserializedSum.takeError(), llvm::Succeeded());
+ EXPECT_EQ(
+ *static_cast<UnsafeBufferUsageEntitySummary *>(DeserializedSum->get()),
+ *Sum);
}
//////////////////////////////////////////////////////////////
>From 0e7f715a4ad9e4a7cd2ab7b222bfc411340982c9 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Mon, 30 Mar 2026 16:34:13 -0700
Subject: [PATCH 21/51] address comments
---
.../UnsafeBufferUsage/UnsafeBufferUsage.h | 69 +----------
.../UnsafeBufferUsage/UnsafeBufferUsage.cpp | 111 ++++++++++++++----
.../Analyses/MockSerialization.h | 96 ---------------
.../UnsafeBufferUsageTest.cpp | 94 +++++----------
4 files changed, 127 insertions(+), 243 deletions(-)
delete mode 100644 clang/unittests/ScalableStaticAnalysisFramework/Analyses/MockSerialization.h
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index faf48b55bfaaf..ca42be4f5a899 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -11,9 +11,8 @@
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
-#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
#include <set>
namespace clang::ssaf {
@@ -41,6 +40,7 @@ class EntityPointerLevel {
friend class UnsafeBufferUsageEntitySummary;
friend class UnsafeBufferUsageTUSummaryExtractor;
+ friend EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
: Entity(Entity), PointerLevel(PointerLevel) {}
@@ -89,13 +89,14 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
const EntityPointerLevelSet UnsafeBuffers;
friend class UnsafeBufferUsageTUSummaryExtractor;
+ friend UnsafeBufferUsageEntitySummary
+ buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet);
+ friend llvm::iterator_range<EntityPointerLevelSet::const_iterator>
+ getUnsafeBuffers(const UnsafeBufferUsageEntitySummary &);
UnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers)
: EntitySummary(), UnsafeBuffers(std::move(UnsafeBuffers)) {}
- static constexpr llvm::StringLiteral SummarySerializationKey =
- "UnsafeBuffers";
-
public:
SummaryName getSummaryName() const override { return summaryName(); };
@@ -110,64 +111,6 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
bool empty() const { return UnsafeBuffers.empty(); }
static SummaryName summaryName() { return SummaryName{"UnsafeBufferUsage"}; }
-
- // A SerializationAPI implementation for a specific Format needs to implement:
- //
- // 1) Abstract data types, Object (map), Array, Value, ..., etc. with
- // supported operations, and
- // 2) EntityIdToFormatFn, and
- // 3) EntityIdFromFotmatFn.
-
- // Format-independent (de-)serialization functions:
- template <typename SerializationAPI>
- typename SerializationAPI::Object static serialize(
- SerializationAPI &SA, const UnsafeBufferUsageEntitySummary &S) {
- using Array = typename SerializationAPI::Array;
- using Object = typename SerializationAPI::Object;
- Array UnsafeBuffersData;
-
- UnsafeBuffersData.reserve(S.UnsafeBuffers.size());
- for (const auto &EPL : S.UnsafeBuffers)
- UnsafeBuffersData.push_back(
- Array{SA.EntityIdToFormat(EPL.getEntity()), EPL.getPointerLevel()});
- return Object{{SummarySerializationKey.data(), std::move(UnsafeBuffersData)}};
- }
-
- template <typename SerializationAPI>
- llvm::Expected<std::unique_ptr<EntitySummary>> static deserialize(
- SerializationAPI &SA, const typename SerializationAPI::Object &Data) {
- using Array = typename SerializationAPI::Array;
- using Object = typename SerializationAPI::Object;
- const Array *UnsafeBuffersData = Data.getArray(SummarySerializationKey.data());
-
- if (!UnsafeBuffersData)
- return llvm::createStringError("expected a json::Object with a key %s",
- SummarySerializationKey.data());
-
- EntityPointerLevelSet EPLs;
-
- for (auto &EltData : *UnsafeBuffersData) {
- const Array *EltDataAsArr = EltData.getAsArray();
-
- if (!EltDataAsArr || EltDataAsArr->size() != 2)
- return llvm::createStringError("expected a json::Array of size 2");
-
- const Object *IdData = (*EltDataAsArr)[0].getAsObject();
- std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
-
- if (!IdData || !PtrLvData)
- return llvm::createStringError(
- "expected a json::Value of integer type");
-
- llvm::Expected<EntityId> Id = SA.EntityIdFromFormat(*IdData);
-
- if (!Id)
- return Id.takeError();
- EPLs.insert(EntityPointerLevel(Id.get(), *PtrLvData));
- }
- return std::make_unique<UnsafeBufferUsageEntitySummary>(
- UnsafeBufferUsageEntitySummary(std::move(EPLs)));
- }
};
} // namespace clang::ssaf
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 41b7c250773bf..903812afb49ff 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -11,41 +11,88 @@
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
#include "clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h" // IWYU pragma: keep
#include "llvm/Support/Error.h"
+#include "llvm/Support/JSON.h"
+#include <cstdint>
using namespace clang;
using namespace ssaf;
+using Array = llvm::json::Array;
+using Object = llvm::json::Object;
namespace {
-struct JSONSerializationAPI {
- using Object = llvm::json::Object;
- using Array = llvm::json::Array;
- using Value = llvm::json::Value;
-
- JSONFormat::EntityIdToJSONFn EntityIdToFormat;
- JSONFormat::EntityIdFromJSONFn EntityIdFromFormat;
-};
+static constexpr llvm::StringLiteral SummarySerializationKey = "UnsafeBuffers";
} // namespace
-// Adapter functions to the current API:
-static llvm::json::Object jsonSerializeFn(const EntitySummary &Sum,
- JSONFormat::EntityIdToJSONFn Fn) {
- JSONSerializationAPI SA{Fn, {}};
- return UnsafeBufferUsageEntitySummary::serialize(
- SA, *static_cast<const UnsafeBufferUsageEntitySummary *>(&Sum));
+namespace clang::ssaf {
+EntityPointerLevel buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
+ return EntityPointerLevel(Id, PtrLv);
+}
+
+UnsafeBufferUsageEntitySummary
+buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers) {
+ return UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers));
+}
+
+llvm::iterator_range<EntityPointerLevelSet::const_iterator>
+getUnsafeBuffers(const UnsafeBufferUsageEntitySummary &S) {
+ return llvm::make_range(S.UnsafeBuffers.begin(), S.UnsafeBuffers.end());
+}
+} // namespace clang::ssaf
+
+Object static serialize(const EntitySummary &S,
+ JSONFormat::EntityIdToJSONFn Fn) {
+ const UnsafeBufferUsageEntitySummary *SS =
+ static_cast<const UnsafeBufferUsageEntitySummary *>(&S);
+ Array UnsafeBuffersData;
+
+ for (const auto &EPL : getUnsafeBuffers(*SS))
+ UnsafeBuffersData.push_back(
+ Array{Fn(EPL.getEntity()), EPL.getPointerLevel()});
+ return Object{{SummarySerializationKey.data(), std::move(UnsafeBuffersData)}};
+}
+
+llvm::Expected<std::unique_ptr<EntitySummary>> static deserializeImpl(
+ const Object &Data, JSONFormat::EntityIdFromJSONFn Fn) {
+ const Array *UnsafeBuffersData =
+ Data.getArray(SummarySerializationKey.data());
+
+ if (!UnsafeBuffersData)
+ return llvm::createStringError("expected a json::Object with a key %s",
+ SummarySerializationKey.data());
+
+ EntityPointerLevelSet EPLs;
+
+ for (auto &EltData : *UnsafeBuffersData) {
+ const Array *EltDataAsArr = EltData.getAsArray();
+
+ if (!EltDataAsArr || EltDataAsArr->size() != 2)
+ return llvm::createStringError("expected a json::Array of size 2");
+
+ const Object *IdData = (*EltDataAsArr)[0].getAsObject();
+ std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
+
+ if (!IdData || !PtrLvData)
+ return llvm::createStringError("expected a json::Value of integer type");
+
+ llvm::Expected<EntityId> Id = Fn(*IdData);
+
+ if (!Id)
+ return Id.takeError();
+ EPLs.insert(buildEntityPointerLevel(Id.get(), *PtrLvData));
+ }
+ return std::make_unique<UnsafeBufferUsageEntitySummary>(
+ buildUnsafeBufferUsageEntitySummary(std::move(EPLs)));
}
-static llvm::Expected<std::unique_ptr<EntitySummary>>
-jsonDeserializeFn(const llvm::json::Object &Data, EntityIdTable &,
- JSONFormat::EntityIdFromJSONFn Fn) {
- JSONSerializationAPI SA{{}, Fn};
- return UnsafeBufferUsageEntitySummary::deserialize<JSONSerializationAPI>(
- SA, Data);
+llvm::Expected<std::unique_ptr<EntitySummary>> static deserialize(
+ const Object &Data, EntityIdTable &, JSONFormat::EntityIdFromJSONFn Fn) {
+ return deserializeImpl(Data, Fn);
}
struct UnsafeBufferUsageJSONFormatInfo : JSONFormat::FormatInfo {
UnsafeBufferUsageJSONFormatInfo()
: JSONFormat::FormatInfo(UnsafeBufferUsageEntitySummary::summaryName(),
- jsonSerializeFn, jsonDeserializeFn) {}
+ serialize, deserialize) {}
};
static llvm::Registry<JSONFormat::FormatInfo>::Add<
@@ -56,3 +103,25 @@ static llvm::Registry<JSONFormat::FormatInfo>::Add<
// NOLINTNEXTLINE(misc-use-internal-linkage)
volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource = 0;
+
+// For unit test:
+extern llvm::Expected<std::unique_ptr<EntitySummary>>
+serializeDeserializeRoundTrip(
+ const UnsafeBufferUsageEntitySummary &S,
+ std::function<uint64_t(EntityId)> IdToIntFn,
+ std::function<llvm::Expected<EntityId>(uint64_t)> IdFromIntFn) {
+
+ auto IdToJson = [&IdToIntFn](EntityId Id) -> Object {
+ return Object({{"@", IdToIntFn(Id)}});
+ };
+ auto IdFromJson =
+ [&IdFromIntFn](const Object &O) -> llvm::Expected<EntityId> {
+ const auto *Int = O.get("@");
+
+ if (Int && Int->getAsUINT64())
+ return IdFromIntFn(*Int->getAsUINT64());
+ return llvm::createStringError("failed to get EntityId from Object");
+ };
+
+ return deserializeImpl(serialize(S, IdToJson), IdFromJson);
+}
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/MockSerialization.h b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/MockSerialization.h
deleted file mode 100644
index dfe61f20bda71..0000000000000
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/MockSerialization.h
+++ /dev/null
@@ -1,96 +0,0 @@
-//===- MockSerialization.h ------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-//
-// A mock implementation of SerializationAPI for format-independent testing of
-// summary serialization
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_MOCKSERIALIZATION_H
-#define LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_MOCKSERIALIZATION_H
-
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/raw_ostream.h"
-#include <initializer_list>
-#include <map>
-#include <optional>
-#include <variant>
-#include <vector>
-
-using namespace llvm;
-using namespace clang;
-using namespace ssaf;
-
-struct MockSerializationAPI {
- struct Value;
- using Array = std::vector<Value>;
-
- struct Object : public std::map<StringRef, Value> {
- Object(std::initializer_list<Object::value_type> IL)
- : std::map<StringRef, Value>(std::move(IL)) {};
-
- const Array *getArray(StringRef Key) const {
- auto I = find(Key);
- if (I == end())
- return nullptr;
- return I->second.getAsArray();
- }
- };
-
- struct Value {
- private:
- std::variant<Object, Array, uint64_t> Data;
-
- public:
- Value(Object Data) : Data(Data) {}
- Value(Array Data) : Data(Data) {}
- Value(uint64_t Data) : Data(Data) {}
-
- const Array *getAsArray() const { return std::get_if<Array>(&Data); };
- const Object *getAsObject() const { return std::get_if<Object>(&Data); };
- std::optional<uint64_t> getAsInteger() const {
- if (auto *Int = std::get_if<uint64_t>(&Data))
- return *Int;
- return std::nullopt;
- };
-
- bool operator==(const Value &Other) const {
- if (std::holds_alternative<Object>(Data))
- return Other.getAsObject() &&
- std::equal_to<std::map<StringRef, Value>>()(
- *getAsObject(), *Other.getAsObject());
- if (std::holds_alternative<Array>(Data))
- return Other.getAsArray() && *getAsArray() == *Other.getAsArray();
- if (std::holds_alternative<uint64_t>(Data))
- return Other.getAsInteger() && *getAsInteger() == *Other.getAsInteger();
- return false;
- }
- };
-
- Object EntityIdToFormat(EntityId Id) {
- auto Iter = llvm::find(IDs, Id);
-
- if (Iter == IDs.end())
- Iter = IDs.insert(IDs.end(), Id);
- return Object{{"@", uint64_t(Iter - IDs.begin())}};
- }
-
- llvm::Expected<EntityId> EntityIdFromFormat(const Object &Data) {
- if (Data.count("@"))
- if (auto Idx = Data.at("@").getAsInteger())
- return IDs[*Idx];
- return llvm::createStringError("failed to convert Data to EntityId");
- }
-
-private:
- std::vector<EntityId> IDs;
-};
-#endif // LLVM_CLANG_UNITTESTS_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_MOCKSERIALIZATION_H
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index fa12f7e924bc3..fc90bf93087de 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "Analyses/MockSerialization.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
@@ -22,12 +21,19 @@
#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
-#include <initializer_list>
+#include <memory>
using namespace clang;
using namespace ssaf;
using testing::UnorderedElementsAre;
+// Declare the test proxy function defined in UnsafeBufferUsage.cpp:
+extern llvm::Expected<std::unique_ptr<EntitySummary>>
+serializeDeserializeRoundTrip(
+ const UnsafeBufferUsageEntitySummary &S,
+ std::function<uint64_t(EntityId)> IdToIntFn,
+ std::function<llvm::Expected<EntityId>(uint64_t)> IdFromIntFn);
+
namespace {
template <typename SomeDecl = NamedDecl>
@@ -193,30 +199,6 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
// (De-)Serialization Tests //
//////////////////////////////////////////////////////////////
-static MockSerializationAPI::Object SerilizedOracle(EntityId P, EntityId Q,
- MockSerializationAPI &MSA) {
- // Oracle serialization data for the example:
- // void foo(int ***p, int ****q, int x) {
- // p[5][5][5];
- // q[5][5][5][5];
- // }
- using Array = MockSerializationAPI::Array;
- using Object = MockSerializationAPI::Object;
-
- Object PData = MSA.EntityIdToFormat(P);
- Object QData = MSA.EntityIdToFormat(Q);
-
- return Object{std::pair{"UnsafeBuffers", Array{
- Array{PData, 1U},
- Array{PData, 2U},
- Array{PData, 3U},
- Array{QData, 1U},
- Array{QData, 2U},
- Array{QData, 3U},
- Array{QData, 4U},
- }}};
-}
-
TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
auto Sum = setUpTest(R"cpp(
void foo(int ***p, int ****q, int x) {
@@ -234,45 +216,31 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
{"q", 3U},
{"q", 4U}}));
- MockSerializationAPI MSA;
- MockSerializationAPI::Object JData =
- UnsafeBufferUsageEntitySummary::serialize<MockSerializationAPI>(MSA,
- *Sum);
-
- auto EntityP = getEntityId("p");
- auto EntityQ = getEntityId("q");
-
- EXPECT_EQ(JData, SerilizedOracle(*EntityP, *EntityQ, MSA));
-}
-
-TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageDeserializeTest) {
- auto Sum = setUpTest(R"cpp(
- void foo(int ***p, int ****q, int x) {
- p[5][5][5];
- q[5][5][5][5];
+ std::vector<EntityId> Table;
+ std::function<uint64_t(EntityId)> IdToIntFn =
+ [&Table](EntityId Id) -> uint64_t {
+ auto I = llvm::find(Table, Id);
+ if (I == Table.end()) {
+ Table.push_back(Id);
+ return Table.size() - 1;
}
- )cpp",
- "foo");
- ASSERT_NE(Sum, nullptr);
- EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U},
- {"p", 2U},
- {"p", 3U},
- {"q", 1U},
- {"q", 2U},
- {"q", 3U},
- {"q", 4U}}));
+ return I - Table.begin();
+ };
+ std::function<llvm::Expected<EntityId>(uint64_t)> IdFromIntFn =
+ [&Table](uint64_t Int) -> llvm::Expected<EntityId> {
+ if (0 <= Int && Int < Table.size())
+ return Table[Int];
+ return llvm::createStringError(
+ "unrecognized dummy index %d for an EntityId", Int);
+ };
+
+ auto RoundTripResult =
+ serializeDeserializeRoundTrip(*Sum, IdToIntFn, IdFromIntFn);
- MockSerializationAPI MSA;
- auto EntityP = getEntityId("p");
- auto EntityQ = getEntityId("q");
- auto DeserializedSum =
- UnsafeBufferUsageEntitySummary::deserialize<MockSerializationAPI>(
- MSA, SerilizedOracle(*EntityP, *EntityQ, MSA));
-
- ASSERT_THAT_ERROR(DeserializedSum.takeError(), llvm::Succeeded());
- EXPECT_EQ(
- *static_cast<UnsafeBufferUsageEntitySummary *>(DeserializedSum->get()),
- *Sum);
+ EXPECT_THAT_ERROR(RoundTripResult.takeError(), llvm::Succeeded());
+ ASSERT_NE(*RoundTripResult, nullptr);
+ EXPECT_EQ(*Sum, *static_cast<const UnsafeBufferUsageEntitySummary *>(
+ RoundTripResult->get()));
}
//////////////////////////////////////////////////////////////
>From e4d0b9ea812b941059ce3d87066212097e59f2e3 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Tue, 31 Mar 2026 18:44:11 -0700
Subject: [PATCH 22/51] address comments
---
.../UnsafeBufferUsage/UnsafeBufferUsage.cpp | 29 +++++++----------
.../Analysis/Scalable/ssaf-format/list.test | 2 +-
.../UnsafeBufferUsageTest.cpp | 31 ++++++++++---------
3 files changed, 29 insertions(+), 33 deletions(-)
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 903812afb49ff..73c20e935d417 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -9,7 +9,6 @@
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
-#include "clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h" // IWYU pragma: keep
#include "llvm/Support/Error.h"
#include "llvm/Support/JSON.h"
#include <cstdint>
@@ -19,40 +18,35 @@ using namespace ssaf;
using Array = llvm::json::Array;
using Object = llvm::json::Object;
-namespace {
static constexpr llvm::StringLiteral SummarySerializationKey = "UnsafeBuffers";
-} // namespace
-namespace clang::ssaf {
-EntityPointerLevel buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
+EntityPointerLevel ssaf::buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
return EntityPointerLevel(Id, PtrLv);
}
UnsafeBufferUsageEntitySummary
-buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers) {
+ssaf::buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers) {
return UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers));
}
llvm::iterator_range<EntityPointerLevelSet::const_iterator>
-getUnsafeBuffers(const UnsafeBufferUsageEntitySummary &S) {
+ssaf::getUnsafeBuffers(const UnsafeBufferUsageEntitySummary &S) {
return llvm::make_range(S.UnsafeBuffers.begin(), S.UnsafeBuffers.end());
}
-} // namespace clang::ssaf
-Object static serialize(const EntitySummary &S,
+static Object serialize(const EntitySummary &S,
JSONFormat::EntityIdToJSONFn Fn) {
- const UnsafeBufferUsageEntitySummary *SS =
- static_cast<const UnsafeBufferUsageEntitySummary *>(&S);
+ const auto &SS = static_cast<const UnsafeBufferUsageEntitySummary &>(S);
Array UnsafeBuffersData;
- for (const auto &EPL : getUnsafeBuffers(*SS))
+ for (const auto &EPL : getUnsafeBuffers(SS))
UnsafeBuffersData.push_back(
Array{Fn(EPL.getEntity()), EPL.getPointerLevel()});
return Object{{SummarySerializationKey.data(), std::move(UnsafeBuffersData)}};
}
-llvm::Expected<std::unique_ptr<EntitySummary>> static deserializeImpl(
- const Object &Data, JSONFormat::EntityIdFromJSONFn Fn) {
+static llvm::Expected<std::unique_ptr<EntitySummary>>
+deserializeImpl(const Object &Data, JSONFormat::EntityIdFromJSONFn Fn) {
const Array *UnsafeBuffersData =
Data.getArray(SummarySerializationKey.data());
@@ -62,7 +56,7 @@ llvm::Expected<std::unique_ptr<EntitySummary>> static deserializeImpl(
EntityPointerLevelSet EPLs;
- for (auto &EltData : *UnsafeBuffersData) {
+ for (const auto &EltData : *UnsafeBuffersData) {
const Array *EltDataAsArr = EltData.getAsArray();
if (!EltDataAsArr || EltDataAsArr->size() != 2)
@@ -84,8 +78,9 @@ llvm::Expected<std::unique_ptr<EntitySummary>> static deserializeImpl(
buildUnsafeBufferUsageEntitySummary(std::move(EPLs)));
}
-llvm::Expected<std::unique_ptr<EntitySummary>> static deserialize(
- const Object &Data, EntityIdTable &, JSONFormat::EntityIdFromJSONFn Fn) {
+static llvm::Expected<std::unique_ptr<EntitySummary>>
+deserialize(const Object &Data, EntityIdTable &,
+ JSONFormat::EntityIdFromJSONFn Fn) {
return deserializeImpl(Data, Fn);
}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/list.test b/clang/test/Analysis/Scalable/ssaf-format/list.test
index 766d6eba1821b..e7ac9196a25f4 100644
--- a/clang/test/Analysis/Scalable/ssaf-format/list.test
+++ b/clang/test/Analysis/Scalable/ssaf-format/list.test
@@ -7,4 +7,4 @@
// CHECK-EMPTY:
// CHECK-NEXT: 1. json - JSON serialization format
// CHECK-NEXT: Analyses:
-// CHECK-DAG: 1.1. UnsafeBufferUsage - JSON Format info for UnsafeBufferUsageEntitySummary
+// CHECK-DAG: {{.*}}. UnsafeBufferUsage - JSON Format info for UnsafeBufferUsageEntitySummary
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index fc90bf93087de..009af529e6a7a 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -7,11 +7,13 @@
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "TestFixture.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
@@ -22,6 +24,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <memory>
+#include <optional>
using namespace clang;
using namespace ssaf;
@@ -69,7 +72,7 @@ const FunctionDecl *findFnByName(StringRef Name, ASTContext &Ctx) {
constexpr inline auto buildEntityPointerLevel =
UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
-class UnsafeBufferUsageTest : public testing::Test {
+class UnsafeBufferUsageTest : public TestFixture {
protected:
TUSummary TUSum;
TUSummaryBuilder Builder;
@@ -216,22 +219,20 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
{"q", 3U},
{"q", 4U}}));
- std::vector<EntityId> Table;
- std::function<uint64_t(EntityId)> IdToIntFn =
- [&Table](EntityId Id) -> uint64_t {
- auto I = llvm::find(Table, Id);
- if (I == Table.end()) {
- Table.push_back(Id);
- return Table.size() - 1;
- }
- return I - Table.begin();
+ std::function<uint64_t(EntityId)> IdToIntFn = [](EntityId Id) -> uint64_t {
+ return getIndex(Id);
};
std::function<llvm::Expected<EntityId>(uint64_t)> IdFromIntFn =
- [&Table](uint64_t Int) -> llvm::Expected<EntityId> {
- if (0 <= Int && Int < Table.size())
- return Table[Int];
- return llvm::createStringError(
- "unrecognized dummy index %d for an EntityId", Int);
+ [this](uint64_t Int) -> llvm::Expected<EntityId> {
+ std::optional<EntityId> Result = std::nullopt;
+
+ getIdTable(TUSum).forEach([&Int, &Result](const EntityName &, EntityId Id) {
+ if (getIndex(Id) == Int)
+ Result = Id;
+ });
+ if (Result)
+ return *Result;
+ return llvm::createStringError("failed to convert %d to an EntityId", Int);
};
auto RoundTripResult =
>From 54e480ee4d9dbff32c902f1c7f384d81ea537bf5 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 1 Apr 2026 16:27:34 -0700
Subject: [PATCH 23/51] address comments
---
.../UnsafeBufferUsage/UnsafeBufferUsage.h | 5 +++-
.../UnsafeBufferUsage/UnsafeBufferUsageTest.h | 27 +++++++++++++++++++
.../UnsafeBufferUsage/UnsafeBufferUsage.cpp | 7 ++---
.../UnsafeBufferUsageTest.cpp | 8 +-----
4 files changed, 36 insertions(+), 11 deletions(-)
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index ca42be4f5a899..9a561b61c3b1a 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -12,6 +12,7 @@
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator_range.h"
#include <set>
@@ -98,6 +99,8 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
: EntitySummary(), UnsafeBuffers(std::move(UnsafeBuffers)) {}
public:
+ static constexpr llvm::StringLiteral Name = "UnsafeBufferUsage";
+
SummaryName getSummaryName() const override { return summaryName(); };
bool operator==(const EntityPointerLevelSet &Other) const {
@@ -110,7 +113,7 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
bool empty() const { return UnsafeBuffers.empty(); }
- static SummaryName summaryName() { return SummaryName{"UnsafeBufferUsage"}; }
+ static SummaryName summaryName() { return SummaryName{Name.data()}; }
};
} // namespace clang::ssaf
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
new file mode 100644
index 0000000000000..883f57c7cfa2c
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
@@ -0,0 +1,27 @@
+//===- UnsafeBufferUsageTest.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Functions and data structures that help UnsafeBufferUsage unit tests
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGETEST_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGETEST_H
+
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
+#include "llvm/Support/Error.h"
+namespace clang::ssaf {
+
+llvm::Expected<std::unique_ptr<EntitySummary>> serializeDeserializeRoundTrip(
+ const UnsafeBufferUsageEntitySummary &S,
+ std::function<uint64_t(EntityId)> IdToIntFn,
+ std::function<llvm::Expected<EntityId>(uint64_t)> IdFromIntFn);
+
+} // namespace clang::ssaf
+
+#endif
\ No newline at end of file
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 73c20e935d417..d325e8df79c20 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
#include "llvm/Support/Error.h"
@@ -93,15 +94,15 @@ struct UnsafeBufferUsageJSONFormatInfo : JSONFormat::FormatInfo {
static llvm::Registry<JSONFormat::FormatInfo>::Add<
UnsafeBufferUsageJSONFormatInfo>
RegisterUnsafeBufferUsageJSONFormatInfo(
- "UnsafeBufferUsage",
+ UnsafeBufferUsageEntitySummary::Name,
"JSON Format info for UnsafeBufferUsageEntitySummary");
// NOLINTNEXTLINE(misc-use-internal-linkage)
volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource = 0;
// For unit test:
-extern llvm::Expected<std::unique_ptr<EntitySummary>>
-serializeDeserializeRoundTrip(
+llvm::Expected<std::unique_ptr<EntitySummary>>
+ssaf::serializeDeserializeRoundTrip(
const UnsafeBufferUsageEntitySummary &S,
std::function<uint64_t(EntityId)> IdToIntFn,
std::function<llvm::Expected<EntityId>(uint64_t)> IdFromIntFn) {
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 009af529e6a7a..91a55700b1356 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
#include "TestFixture.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/Frontend/ASTUnit.h"
@@ -30,13 +31,6 @@ using namespace clang;
using namespace ssaf;
using testing::UnorderedElementsAre;
-// Declare the test proxy function defined in UnsafeBufferUsage.cpp:
-extern llvm::Expected<std::unique_ptr<EntitySummary>>
-serializeDeserializeRoundTrip(
- const UnsafeBufferUsageEntitySummary &S,
- std::function<uint64_t(EntityId)> IdToIntFn,
- std::function<llvm::Expected<EntityId>(uint64_t)> IdFromIntFn);
-
namespace {
template <typename SomeDecl = NamedDecl>
>From 68eddbb18fa01a2d70f18294d60eadeea9c070b0 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 10:29:41 -0700
Subject: [PATCH 24/51] Update
clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
index 883f57c7cfa2c..f198d7624ff82 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
@@ -1,4 +1,4 @@
-//===- UnsafeBufferUsageTest.h -----------------------------*- C++ -*-===//
+//===- UnsafeBufferUsageTest.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.
>From 70c1962df47f436d51f6b96c67f5fe8292293509 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 10:31:40 -0700
Subject: [PATCH 25/51] Update
clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
index f198d7624ff82..ccdee91182dc0 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Functions and data structures that help UnsafeBufferUsage unit tests
+// Functions and data structures that help UnsafeBufferUsage unit tests.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGETEST_H
>From 1b980e089b556c0c7adba88f7e9725bc515d73ca Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 10:32:53 -0700
Subject: [PATCH 26/51] Update
clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
index ccdee91182dc0..b62dcee553c8d 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
@@ -9,8 +9,9 @@
// Functions and data structures that help UnsafeBufferUsage unit tests.
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGETEST_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGETEST_H
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGETEST_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGETEST_H
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
>From b220e30eab659495f3d05a156257bdb442444f53 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 10:34:27 -0700
Subject: [PATCH 27/51] Update
clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
index b62dcee553c8d..b938a94a4348f 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
@@ -16,6 +16,7 @@
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
#include "llvm/Support/Error.h"
+
namespace clang::ssaf {
llvm::Expected<std::unique_ptr<EntitySummary>> serializeDeserializeRoundTrip(
>From 9eebb20e9f0733da4b69bcd9cc6bfcd9887da799 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 10:34:59 -0700
Subject: [PATCH 28/51] Update
clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
index b938a94a4348f..6a2d1161246f2 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h
@@ -26,4 +26,4 @@ llvm::Expected<std::unique_ptr<EntitySummary>> serializeDeserializeRoundTrip(
} // namespace clang::ssaf
-#endif
\ No newline at end of file
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGETEST_H
>From 74863f2b2901b0205dc84db0e9bda40316852713 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 10:35:35 -0700
Subject: [PATCH 29/51] Update
clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 9a561b61c3b1a..4e217c2eb87ef 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -113,7 +113,7 @@ class UnsafeBufferUsageEntitySummary final : public EntitySummary {
bool empty() const { return UnsafeBuffers.empty(); }
- static SummaryName summaryName() { return SummaryName{Name.data()}; }
+ static SummaryName summaryName() { return SummaryName{Name.str()}; }
};
} // namespace clang::ssaf
>From ce07c3cefef5c5875f6bbb340f2abcf4ab47e9b9 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 10:36:37 -0700
Subject: [PATCH 30/51] Update
clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Balázs Benics <benicsbalazs at gmail.com>
---
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 91a55700b1356..51d422c1921af 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -7,11 +7,11 @@
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
#include "TestFixture.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.h"
>From b44a1107845fe00cc16fe2ef8cc8ab2fe7ce30ff Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 11:26:08 -0700
Subject: [PATCH 31/51] [NFC][SSAF][UnsafeBufferUsage] Separate
EntityPointerLevel and UnsafeBufferUsage
EntityPointerLevel as a common data structure will later be shared by
UnsafeBufferUsage and pointer assignments analysis. So this commit
makes them separate:
- EntityPointerLevel provides the data structure and translation
- UnsafeBufferUsage uses EntityPointerLevel to translate unsafe pointers to EPLs.
---
.../Analyses/EntityPointerLevel.h | 134 +++++++
.../UnsafeBufferUsage/UnsafeBufferUsage.h | 76 +---
.../UnsafeBufferUsageExtractor.h | 5 -
.../Analyses/CMakeLists.txt | 1 +
.../Analyses/EntityPointerLevel.cpp | 332 ++++++++++++++++++
.../UnsafeBufferUsageExtractor.cpp | 225 +-----------
.../UnsafeBufferUsageTest.cpp | 4 +-
7 files changed, 480 insertions(+), 297 deletions(-)
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
new file mode 100644
index 0000000000000..945af7d6c9f3f
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
@@ -0,0 +1,134 @@
+//===- EntityPointerLevel.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
+
+#include "clang/AST/Decl.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+#include <set>
+
+namespace clang::ssaf {
+
+/// An EntityPointerLevel is associated with a level of the declared
+/// pointer/array type of an entity. In the fully-expanded spelling of the
+/// declared type, a EntityPointerLevel is associated with a '*' (or a '[]`) in
+/// that declaration.
+///
+/// For example, for 'int *p[10];', there are two EntityPointerLevels. One
+/// is associated with 'int *[10]' of 'p' and the other is associated with 'int
+/// *' of 'p'.
+///
+/// An EntityPointerLevel can be identified by an EntityId and an unsigned
+/// integer indicating the pointer level: '(EntityId, PointerLevel)'.
+/// An EntityPointerLevel 'P' is valid iff 'P.EntityId' has a pointer type with
+/// at least 'P.PointerLevel' levels (This implies 'P.PointerLevel > 0').
+///
+/// For the same example 'int *p[10];', the EntityPointerLevels below are valid:
+/// - '(p, 2)' is associated with the 'int *' part of the declared type of 'p';
+/// - '(p, 1)' is associated with the 'int *[10]' part of the declared type of
+/// 'p'.
+class EntityPointerLevel {
+ EntityId Entity;
+ unsigned PointerLevel;
+
+ friend class EntityPointerLevelTranslator;
+ friend llvm::Expected<EntityPointerLevel>
+ entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
+ // For unittests:
+ friend EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
+
+ // EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
+ // : Entity(Entity), PointerLevel(PointerLevel) {}
+ EntityPointerLevel(std::pair<EntityId, unsigned> Pair)
+ : Entity(Pair.first), PointerLevel(Pair.second) {}
+
+public:
+ EntityId getEntity() const { return Entity; }
+ unsigned getPointerLevel() const { return PointerLevel; }
+
+ bool operator==(const EntityPointerLevel &Other) const {
+ return std::tie(Entity, PointerLevel) ==
+ std::tie(Other.Entity, Other.PointerLevel);
+ }
+
+ bool operator!=(const EntityPointerLevel &Other) const {
+ return !(*this == Other);
+ }
+
+ bool operator<(const EntityPointerLevel &Other) const {
+ return std::tie(Entity, PointerLevel) <
+ std::tie(Other.Entity, Other.PointerLevel);
+ }
+
+ /// Compares `EntityPointerLevel`s; additionally, partially compares
+ /// `EntityPointerLevel` with `EntityId`.
+ struct Comparator {
+ using is_transparent = void;
+ bool operator()(const EntityPointerLevel &L,
+ const EntityPointerLevel &R) const {
+ return L < R;
+ }
+ bool operator()(const EntityId &L, const EntityPointerLevel &R) const {
+ return L < R.getEntity();
+ }
+ bool operator()(const EntityPointerLevel &L, const EntityId &R) const {
+ return L.getEntity() < R;
+ }
+ };
+};
+
+using EntityPointerLevelSet =
+ std::set<EntityPointerLevel, EntityPointerLevel::Comparator>;
+
+/// Translate a pointer/array type expression 'E' to a (set of)
+/// EntityPointerLevel(s) associated with the declared type of the base address
+/// of `E`. If the base address of `E` is not associated with an entity, the
+/// translation result is an empty set.
+///
+/// \param E the pointer expression to be translated
+/// \param Ctx the AST context of `E`
+/// \param AddEntity the callback provided by the caller to convert EntityNames
+/// to EntityIds.
+llvm::Expected<EntityPointerLevelSet>
+translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
+ std::function<EntityId(EntityName EN)> AddEntity);
+
+/// Create an EntityPointerLevel (EPL) from a NamedDecl of a pointer/array type.
+///
+/// \param E the pointer expression to be translated
+/// \param Ctx the AST context of `E`
+/// \param AddEntity the callback provided by the caller to convert EntityNames
+/// to EntityIds.
+/// \param IsFunRet true iff the created EPL is associated with the return type
+/// of a function entity.
+llvm::Expected<EntityPointerLevel>
+creatEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
+ std::function<EntityId(EntityName EN)> AddEntity,
+ bool IsFunRet = false);
+
+/// Creates a new EntityPointerLevel (EPL) from `E` by incrementing `E`'s
+/// pointer level.
+/// \return the EPL that is associated with the pointee (or array element) type
+/// of `E`'s associated pointer/array tyoe of the same entity.
+EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E);
+
+llvm::json::Value
+entityPointerLevelToJSON(const EntityPointerLevel &EPL,
+ JSONFormat::EntityIdToJSONFn EntityId2JSON);
+
+llvm::Expected<EntityPointerLevel>
+entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
+
+/// Proxy function creating EPLs for unit tests:
+EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
+} // namespace clang::ssaf
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 4e217c2eb87ef..9b85459a284eb 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -9,83 +9,13 @@
#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/iterator_range.h"
-#include <set>
namespace clang::ssaf {
-
-/// An EntityPointerLevel represents a level of the declared pointer/array
-/// type of an entity. In the fully-expanded spelling of the declared type, a
-/// EntityPointerLevel is associated with a '*' (or a '[]`) in that declaration.
-///
-/// For example, for 'int *p[10];', there are two EntityPointerLevels. One
-/// is associated with 'int *[10]' of 'p' and the other is associated with 'int
-/// *' of 'p'.
-///
-/// An EntityPointerLevel can be identified by an EntityId and an unsigned
-/// integer indicating the pointer level: '(EntityId, PointerLevel)'.
-/// An EntityPointerLevel 'P' is valid iff 'P.EntityId' has a pointer type with
-/// at least 'P.PointerLevel' levels (This implies 'P.PointerLevel > 0').
-///
-/// For the same example 'int *p[10];', the EntityPointerLevels below are valid:
-/// - '(p, 2)' is associated with the 'int *' part of the declared type of 'p';
-/// - '(p, 1)' is associated with the 'int *[10]' part of the declared type of
-/// 'p'.
-class EntityPointerLevel {
- EntityId Entity;
- unsigned PointerLevel;
-
- friend class UnsafeBufferUsageEntitySummary;
- friend class UnsafeBufferUsageTUSummaryExtractor;
- friend EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
-
- EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
- : Entity(Entity), PointerLevel(PointerLevel) {}
-
-public:
- EntityId getEntity() const { return Entity; }
- unsigned getPointerLevel() const { return PointerLevel; }
-
- bool operator==(const EntityPointerLevel &Other) const {
- return std::tie(Entity, PointerLevel) ==
- std::tie(Other.Entity, Other.PointerLevel);
- }
-
- bool operator!=(const EntityPointerLevel &Other) const {
- return !(*this == Other);
- }
-
- bool operator<(const EntityPointerLevel &Other) const {
- return std::tie(Entity, PointerLevel) <
- std::tie(Other.Entity, Other.PointerLevel);
- }
-
- /// Compares `EntityPointerLevel`s; additionally, partially compares
- /// `EntityPointerLevel` with `EntityId`.
- struct Comparator {
- using is_transparent = void;
- bool operator()(const EntityPointerLevel &L,
- const EntityPointerLevel &R) const {
- return L < R;
- }
- bool operator()(const EntityId &L, const EntityPointerLevel &R) const {
- return L < R.getEntity();
- }
- bool operator()(const EntityPointerLevel &L, const EntityId &R) const {
- return L.getEntity() < R;
- }
- };
-};
-
-using EntityPointerLevelSet =
- std::set<EntityPointerLevel, EntityPointerLevel::Comparator>;
-
-/// An UnsafeBufferUsageEntitySummary is an immutable set of unsafe buffers, in
-/// the form of EntityPointerLevel.
+/// An UnsafeBufferUsageEntitySummary contains a set of EntityPointerLevels
+/// extracted from unsafe buffer pointers contributed by an entity.
class UnsafeBufferUsageEntitySummary final : public EntitySummary {
const EntityPointerLevelSet UnsafeBuffers;
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
index 765b2c37562ce..13d4e18b4e81f 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h
@@ -22,11 +22,6 @@ class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor {
UnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder)
: TUSummaryExtractor(Builder) {}
- static EntityPointerLevel buildEntityPointerLevel(EntityId Entity,
- unsigned PointerLevel) {
- return {Entity, PointerLevel};
- }
-
EntityId addEntity(EntityName EN) { return SummaryBuilder.addEntity(EN); }
std::unique_ptr<UnsafeBufferUsageEntitySummary>
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
index 926b610aa8dee..68107ce36ab99 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.cpp
UnsafeBufferUsage/UnsafeBufferUsage.cpp
UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
new file mode 100644
index 0000000000000..e607529342351
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
@@ -0,0 +1,332 @@
+//===----------------- EntityPointerLevel.cpp -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include <optional>
+
+namespace {
+using namespace clang;
+template <typename NodeTy, typename... Ts>
+static inline llvm::Error strErrAtNode(ASTContext &Ctx, const NodeTy &N,
+ StringRef Fmt, const Ts &...Args) {
+ std::string LocStr = N.getBeginLoc().printToString(Ctx.getSourceManager());
+ llvm::SmallVector<char> FmtData;
+
+ (Fmt + " at %s").toStringRef(FmtData);
+ return llvm::createStringError(FmtData.data(), Args..., LocStr.c_str());
+}
+
+static inline llvm::Error entityNameErrFor(ASTContext &Ctx,
+ const NamedDecl &D) {
+ return strErrAtNode(Ctx, D, "failed to create entity name for %s",
+ D.getNameAsString().data());
+}
+
+template <typename DeclOrExpr>
+static bool hasPtrOrArrType(const DeclOrExpr &E) {
+ return llvm::isa<PointerType>(E.getType().getCanonicalType()) ||
+ llvm::isa<ArrayType>(E.getType().getCanonicalType());
+}
+
+template <typename... Ts>
+static inline llvm::Error makeErrorSawButExpected(const llvm::json::Value &Saw,
+ llvm::StringRef Expected,
+ const Ts &...ExpectedArgs) {
+ return llvm::createStringError(
+ ("saw %s but expected " + Expected).str().c_str(),
+ llvm::formatv("{0:2}", Saw).str().data(), Expected.data(),
+ ExpectedArgs...);
+}
+} // namespace
+
+namespace clang::ssaf {
+// Translate a pointer type expression 'E' to a (set of) EntityPointerLevel(s)
+// associated with the declared type of the base address of `E`. If the base
+// address of `E` is not associated with an entity, the translation result is an
+// empty set.
+//
+// The translation is a process of traversing into the pointer 'E' until its
+// base address can be represented by an entity, with the number of dereferences
+// tracked by incrementing the pointer level. Naturally, taking address of, as
+// the inverse operation of dereference, is tracked by decrementing the pointer
+// level.
+//
+// For example, suppose there are pointers and arrays declared as
+// int *ptr, **p1, **p2;
+// int arr[10][10];
+// , the translation of expressions involving these base addresses will be:
+// Translate(ptr + 5) -> {(ptr, 1)}
+// Translate(arr[5]) -> {(arr, 2)}
+// Translate(cond ? p1[5] : p2) -> {(p1, 2), (p2, 1)}
+// Translate(&arr[5]) -> {(arr, 1)}
+class EntityPointerLevelTranslator
+ : public ConstStmtVisitor<EntityPointerLevelTranslator,
+ Expected<EntityPointerLevelSet>> {
+ friend class StmtVisitorBase;
+
+ // Fallback method for all unsupported expression kind:
+ llvm::Error fallback(const Stmt *E) {
+ return strErrAtNode(Ctx, *E,
+ "attempt to translate %s to EntityPointerLevels",
+ E->getStmtClassName());
+ }
+
+ EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) {
+ return EntityPointerLevel({AddEntity(Name), 1});
+ }
+
+ // The common helper function for Translate(*base):
+ // Translate(*base) -> Translate(base) with .pointerLevel + 1
+ Expected<EntityPointerLevelSet> translateDereferencePointer(const Expr *Ptr) {
+ assert(hasPtrOrArrType(*Ptr));
+
+ Expected<EntityPointerLevelSet> SubResult = Visit(Ptr);
+ if (!SubResult)
+ return SubResult.takeError();
+
+ auto Incremented = llvm::map_range(*SubResult, incrementPointerLevel);
+ return EntityPointerLevelSet{Incremented.begin(), Incremented.end()};
+ }
+
+ std::function<EntityId(EntityName EN)> AddEntity;
+ ASTContext &Ctx;
+
+public:
+ EntityPointerLevelTranslator(std::function<EntityId(EntityName EN)> AddEntity,
+ ASTContext &Ctx)
+ : AddEntity(AddEntity), Ctx(Ctx) {}
+
+ Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
+ Expected<EntityPointerLevel> translate(const NamedDecl *D, bool IsRet) {
+ if (IsRet && !isa<FunctionDecl>(D))
+ return strErrAtNode(
+ Ctx, *D,
+ "attempt to call getEntityNameForReturn on a NamedDecl of %s kind",
+ D->getDeclKindName());
+
+ std::optional<EntityName> EN =
+ IsRet ? getEntityNameForReturn(cast<FunctionDecl>(D))
+ : getEntityName(D);
+ if (EN)
+ return createEntityPointerLevelFor(*EN);
+ return entityNameErrFor(Ctx, *D);
+ }
+
+ static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
+ return EntityPointerLevel({E.getEntity(), E.getPointerLevel() + 1});
+ }
+
+ static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) {
+ assert(E.getPointerLevel() > 0);
+ return EntityPointerLevel({E.getEntity(), E.getPointerLevel() - 1});
+ }
+
+private:
+ Expected<EntityPointerLevelSet> VisitStmt(const Stmt *E) {
+ return fallback(E);
+ }
+
+ // Translate(base + x) -> Translate(base)
+ // Translate(x + base) -> Translate(base)
+ // Translate(base - x) -> Translate(base)
+ // Translate(base {+=, -=, =} x) -> Translate(base)
+ // Translate(x, base) -> Translate(base)
+ Expected<EntityPointerLevelSet> VisitBinaryOperator(const BinaryOperator *E) {
+ switch (E->getOpcode()) {
+ case clang::BO_Add:
+ if (hasPtrOrArrType(*E->getLHS()))
+ return Visit(E->getLHS());
+ return Visit(E->getRHS());
+ case clang::BO_Sub:
+ case clang::BO_AddAssign:
+ case clang::BO_SubAssign:
+ case clang::BO_Assign:
+ return Visit(E->getLHS());
+ case clang::BO_Comma:
+ return Visit(E->getRHS());
+ default:
+ return fallback(E);
+ }
+ }
+
+ // Translate({++, --}base) -> Translate(base)
+ // Translate(base{++, --}) -> Translate(base)
+ // Translate(*base) -> Translate(base) with .pointerLevel += 1
+ // Translate(&base) -> {}, if Translate(base) is {}
+ // -> Translate(base) with .pointerLevel -= 1
+ Expected<EntityPointerLevelSet> VisitUnaryOperator(const UnaryOperator *E) {
+ switch (E->getOpcode()) {
+ case clang::UO_PostInc:
+ case clang::UO_PostDec:
+ case clang::UO_PreInc:
+ case clang::UO_PreDec:
+ return Visit(E->getSubExpr());
+ case clang::UO_AddrOf: {
+ Expected<EntityPointerLevelSet> SubResult = Visit(E->getSubExpr());
+ if (!SubResult)
+ return SubResult.takeError();
+
+ auto Decremented = llvm::map_range(*SubResult, decrementPointerLevel);
+ return EntityPointerLevelSet{Decremented.begin(), Decremented.end()};
+ }
+ case clang::UO_Deref:
+ return translateDereferencePointer(E->getSubExpr());
+ default:
+ return fallback(E);
+ }
+ }
+
+ // Translate((T*)base) -> Translate(p) if p has pointer type
+ // -> {} otherwise
+ Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) {
+ if (hasPtrOrArrType(*E->getSubExpr()))
+ return Visit(E->getSubExpr());
+ return EntityPointerLevelSet{};
+ }
+
+ // Translate(f(...)) -> {} if it is an indirect call
+ // -> {(f_return, 1)}, otherwise
+ Expected<EntityPointerLevelSet> VisitCallExpr(const CallExpr *E) {
+ if (auto *FD = E->getDirectCallee())
+ if (auto FDEntityName = getEntityNameForReturn(FD))
+ return EntityPointerLevelSet{
+ createEntityPointerLevelFor(*FDEntityName)};
+ return EntityPointerLevelSet{};
+ }
+
+ // Translate(base[x]) -> Translate(*base)
+ Expected<EntityPointerLevelSet>
+ VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+ return translateDereferencePointer(E->getBase());
+ }
+
+ // Translate(cond ? base1 : base2) := Translate(base1) U Translate(base2)
+ Expected<EntityPointerLevelSet>
+ VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
+ Expected<EntityPointerLevelSet> ReT = Visit(E->getTrueExpr());
+ Expected<EntityPointerLevelSet> ReF = Visit(E->getFalseExpr());
+
+ if (ReT && ReF) {
+ ReT->insert(ReF->begin(), ReF->end());
+ return ReT;
+ }
+ if (!ReF && !ReT)
+ return llvm::joinErrors(ReT.takeError(), ReF.takeError());
+ if (!ReF)
+ return ReF.takeError();
+ return ReT.takeError();
+ }
+
+ Expected<EntityPointerLevelSet> VisitParenExpr(const ParenExpr *E) {
+ return Visit(E->getSubExpr());
+ }
+
+ // Translate("string-literal") -> {}
+ // Buffer accesses on string literals are unsafe, but string literals are not
+ // entities so there is no EntityPointerLevel associated with it.
+ Expected<EntityPointerLevelSet> VisitStringLiteral(const StringLiteral *E) {
+ return EntityPointerLevelSet{};
+ }
+
+ // Translate(DRE) -> {(Decl, 1)}
+ Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
+ if (auto EntityName = getEntityName(E->getDecl()))
+ return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
+ return entityNameErrFor(Ctx, *E->getDecl());
+ }
+
+ // Translate({., ->}f) -> {(MemberDecl, 1)}
+ Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
+ if (auto EntityName = getEntityName(E->getMemberDecl()))
+ return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
+ return entityNameErrFor(Ctx, *E->getMemberDecl());
+ }
+
+ // Translate(`DefaultArg`) -> Translate(`DefaultArg->getExpr()`)
+ Expected<EntityPointerLevelSet>
+ VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) {
+ return Visit(E->getExpr());
+ }
+
+ Expected<EntityPointerLevelSet>
+ VisitOpaqueValueExpr(const OpaqueValueExpr *S) {
+ return Visit(S->getSourceExpr());
+ }
+};
+
+Expected<EntityPointerLevelSet>
+translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
+ std::function<EntityId(EntityName EN)> AddEntity) {
+ EntityPointerLevelTranslator Translator(AddEntity, Ctx);
+
+ return Translator.translate(E);
+}
+
+/// Create an EntityPointerLevel from a ValueDecl of a pointer type.
+Expected<EntityPointerLevel>
+creatEntityPointerLevel(const NamedDecl *D, ASTContext &Ctx,
+ std::function<EntityId(EntityName EN)> AddEntity,
+ bool IsFunRet) {
+ EntityPointerLevelTranslator Translator(AddEntity, Ctx);
+
+ return Translator.translate(D, IsFunRet);
+}
+
+EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
+ return EntityPointerLevelTranslator::incrementPointerLevel(E);
+}
+
+// Writes an EntityPointerLevel as
+// Array [
+// Object { "@" : [entity-id]},
+// [pointer-level-integer]
+// ]
+llvm::json::Value
+entityPointerLevelToJSON(const EntityPointerLevel &EPL,
+ JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+ return llvm::json::Array{EntityId2JSON(EPL.getEntity()),
+ llvm::json::Value(EPL.getPointerLevel())};
+}
+
+Expected<EntityPointerLevel>
+entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
+ auto *AsArr = EPLData.getAsArray();
+
+ if (!AsArr || AsArr->size() != 2)
+ return makeErrorSawButExpected(
+ EPLData, "a JSON array of size 2: [EntityId, PointerLevel]");
+
+ auto *EntityIdObj = (*AsArr)[0].getAsObject();
+
+ if (!EntityIdObj)
+ return makeErrorSawButExpected((*AsArr)[0],
+ "a JSON object representing EntityID");
+
+ Expected<EntityId> Id = EntityIdFromJSON(*EntityIdObj);
+
+ if (!Id)
+ return Id.takeError();
+
+ std::optional<uint64_t> PtrLv = (*AsArr)[1].getAsInteger();
+
+ if (!PtrLv)
+ return makeErrorSawButExpected((*AsArr)[1],
+ "a JSON value representing an integer");
+
+ return EntityPointerLevel{std::tie(*Id, *PtrLv)};
+}
+
+EntityPointerLevel buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
+ return EntityPointerLevel({Id, PtrLv});
+}
+
+} // namespace clang::ssaf
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index c609168e4dc7d..aeab9fc8867a3 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -9,14 +9,13 @@
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DynamicRecursiveASTVisitor.h"
-#include "clang/AST/StmtVisitor.h"
#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Error.h"
#include <memory>
@@ -24,22 +23,6 @@ namespace {
using namespace clang;
using namespace ssaf;
-static bool hasPointerType(const Expr *E) {
- auto Ty = E->getType();
- return !Ty.isNull() && !Ty->isFunctionPointerType() &&
- (Ty->isPointerType() || Ty->isArrayType());
-}
-
-constexpr inline auto buildEntityPointerLevel =
- UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel;
-
-static llvm::Error makeUnsupportedStmtKindError(const Stmt *Unsupported) {
- return llvm::createStringError(
- "unsupported expression kind for translation to "
- "EntityPointerLevel: %s",
- Unsupported->getStmtClassName());
-}
-
static llvm::Error makeCreateEntityNameError(const NamedDecl *FailedDecl,
ASTContext &Ctx) {
std::string LocStr = FailedDecl->getSourceRange().getBegin().printToString(
@@ -59,208 +42,17 @@ static llvm::Error makeAddEntitySummaryError(const NamedDecl *FailedContributor,
FailedContributor->getNameAsString().c_str(), LocStr.c_str());
}
-// Translate a pointer type expression 'E' to a (set of) EntityPointerLevel(s)
-// associated with the declared type of the base address of `E`. If the base
-// address of `E` is not associated with an entity, the translation result is an
-// empty set.
-//
-// The translation is a process of traversing into the pointer 'E' until its
-// base address can be represented by an entity, with the number of dereferences
-// tracked by incrementing the pointer level. Naturally, taking address of, as
-// the inverse operation of dereference, is tracked by decrementing the pointer
-// level.
-//
-// For example, suppose there are pointers and arrays declared as
-// int *ptr, **p1, **p2;
-// int arr[10][10];
-// , the translation of expressions involving these base addresses will be:
-// Translate(ptr + 5) -> {(ptr, 1)}
-// Translate(arr[5]) -> {(arr, 2)}
-// Translate(cond ? p1[5] : p2) -> {(p1, 2), (p2, 1)}
-// Translate(&arr[5]) -> {(arr, 1)}
-class EntityPointerLevelTranslator
- : ConstStmtVisitor<EntityPointerLevelTranslator,
- Expected<EntityPointerLevelSet>> {
- friend class StmtVisitorBase;
-
- // Fallback method for all unsupported expression kind:
- llvm::Error fallback(const Stmt *E) {
- return makeUnsupportedStmtKindError(E);
- }
-
- static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
- return buildEntityPointerLevel(E.getEntity(), E.getPointerLevel() + 1);
- }
-
- static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) {
- assert(E.getPointerLevel() > 0);
- return buildEntityPointerLevel(E.getEntity(), E.getPointerLevel() - 1);
- }
-
- EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) {
- return buildEntityPointerLevel(Extractor.addEntity(Name), 1);
- }
-
- // The common helper function for Translate(*base):
- // Translate(*base) -> Translate(base) with .pointerLevel + 1
- Expected<EntityPointerLevelSet> translateDereferencePointer(const Expr *Ptr) {
- assert(hasPointerType(Ptr));
-
- Expected<EntityPointerLevelSet> SubResult = Visit(Ptr);
- if (!SubResult)
- return SubResult.takeError();
-
- auto Incremented = llvm::map_range(*SubResult, incrementPointerLevel);
- return EntityPointerLevelSet{Incremented.begin(), Incremented.end()};
- }
-
- UnsafeBufferUsageTUSummaryExtractor &Extractor;
- ASTContext &Ctx;
-
-public:
- EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor,
- ASTContext &Ctx)
- : Extractor(Extractor), Ctx(Ctx) {}
-
- Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
-
-private:
- Expected<EntityPointerLevelSet> VisitStmt(const Stmt *E) {
- return fallback(E);
- }
-
- // Translate(base + x) -> Translate(base)
- // Translate(x + base) -> Translate(base)
- // Translate(base - x) -> Translate(base)
- // Translate(base {+=, -=, =} x) -> Translate(base)
- // Translate(x, base) -> Translate(base)
- Expected<EntityPointerLevelSet> VisitBinaryOperator(const BinaryOperator *E) {
- switch (E->getOpcode()) {
- case clang::BO_Add:
- if (hasPointerType(E->getLHS()))
- return Visit(E->getLHS());
- return Visit(E->getRHS());
- case clang::BO_Sub:
- case clang::BO_AddAssign:
- case clang::BO_SubAssign:
- case clang::BO_Assign:
- return Visit(E->getLHS());
- case clang::BO_Comma:
- return Visit(E->getRHS());
- default:
- return fallback(E);
- }
- }
-
- // Translate({++, --}base) -> Translate(base)
- // Translate(base{++, --}) -> Translate(base)
- // Translate(*base) -> Translate(base) with .pointerLevel += 1
- // Translate(&base) -> {}, if Translate(base) is {}
- // -> Translate(base) with .pointerLevel -= 1
- Expected<EntityPointerLevelSet> VisitUnaryOperator(const UnaryOperator *E) {
- switch (E->getOpcode()) {
- case clang::UO_PostInc:
- case clang::UO_PostDec:
- case clang::UO_PreInc:
- case clang::UO_PreDec:
- return Visit(E->getSubExpr());
- case clang::UO_AddrOf: {
- Expected<EntityPointerLevelSet> SubResult = Visit(E->getSubExpr());
- if (!SubResult)
- return SubResult.takeError();
-
- auto Decremented = llvm::map_range(*SubResult, decrementPointerLevel);
- return EntityPointerLevelSet{Decremented.begin(), Decremented.end()};
- }
- case clang::UO_Deref:
- return translateDereferencePointer(E->getSubExpr());
- default:
- return fallback(E);
- }
- }
-
- // Translate((T*)base) -> Translate(p) if p has pointer type
- // -> {} otherwise
- Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) {
- if (hasPointerType(E->getSubExpr()))
- return Visit(E->getSubExpr());
- return EntityPointerLevelSet{};
- }
-
- // Translate(f(...)) -> {} if it is an indirect call
- // -> {(f_return, 1)}, otherwise
- Expected<EntityPointerLevelSet> VisitCallExpr(const CallExpr *E) {
- if (auto *FD = E->getDirectCallee())
- if (auto FDEntityName = getEntityNameForReturn(FD))
- return EntityPointerLevelSet{
- createEntityPointerLevelFor(*FDEntityName)};
- return EntityPointerLevelSet{};
- }
-
- // Translate(base[x]) -> Translate(*base)
- Expected<EntityPointerLevelSet>
- VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
- return translateDereferencePointer(E->getBase());
- }
-
- // Translate(cond ? base1 : base2) := Translate(base1) U Translate(base2)
- Expected<EntityPointerLevelSet>
- VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
- Expected<EntityPointerLevelSet> ReT = Visit(E->getTrueExpr());
- Expected<EntityPointerLevelSet> ReF = Visit(E->getFalseExpr());
-
- if (ReT && ReF) {
- ReT->insert(ReF->begin(), ReF->end());
- return ReT;
- }
- if (!ReF && !ReT)
- return llvm::joinErrors(ReT.takeError(), ReF.takeError());
- if (!ReF)
- return ReF.takeError();
- return ReT.takeError();
- }
-
- Expected<EntityPointerLevelSet> VisitParenExpr(const ParenExpr *E) {
- return Visit(E->getSubExpr());
- }
-
- // Translate("string-literal") -> {}
- // Buffer accesses on string literals are unsafe, but string literals are not
- // entities so there is no EntityPointerLevel associated with it.
- Expected<EntityPointerLevelSet> VisitStringLiteral(const StringLiteral *E) {
- return EntityPointerLevelSet{};
- }
-
- // Translate(DRE) -> {(Decl, 1)}
- Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
- if (auto EntityName = getEntityName(E->getDecl()))
- return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
- return makeCreateEntityNameError(E->getDecl(), Ctx);
- }
-
- // Translate({., ->}f) -> {(MemberDecl, 1)}
- Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
- if (auto EntityName = getEntityName(E->getMemberDecl()))
- return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
- return makeCreateEntityNameError(E->getMemberDecl(), Ctx);
- }
-
- Expected<EntityPointerLevelSet>
- VisitOpaqueValueExpr(const OpaqueValueExpr *S) {
- return Visit(S->getSourceExpr());
- }
-};
-
Expected<EntityPointerLevelSet>
buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
UnsafeBufferUsageTUSummaryExtractor &Extractor,
- ASTContext &Ctx) {
+ ASTContext &Ctx,
+ std::function<EntityId(EntityName)> AddEntity) {
EntityPointerLevelSet Result{};
- EntityPointerLevelTranslator Translator{Extractor, Ctx};
llvm::Error AllErrors = llvm::ErrorSuccess();
for (const Expr *Ptr : UnsafePointers) {
- Expected<EntityPointerLevelSet> Translation = Translator.translate(Ptr);
+ Expected<EntityPointerLevelSet> Translation =
+ translateEntityPointerLevel(Ptr, Ctx, AddEntity);
if (Translation) {
// Filter out those temporary invalid EntityPointerLevels associated with
@@ -297,8 +89,9 @@ static std::set<const Expr *> findUnsafePointersInContributor(const Decl *D) {
std::unique_ptr<UnsafeBufferUsageEntitySummary>
UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary(
const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) {
+ auto AddEntity = [this](EntityName EN) { return addEntity(EN); };
Expected<EntityPointerLevelSet> EPLs = buildEntityPointerLevels(
- findUnsafePointersInContributor(Contributor), *this, Ctx);
+ findUnsafePointersInContributor(Contributor), *this, Ctx, AddEntity);
if (EPLs)
return std::make_unique<UnsafeBufferUsageEntitySummary>(
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 51d422c1921af..f14f3caa1e016 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.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 f3c29ee58bbc464b98389296eec5e0c899007324 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 1 Apr 2026 18:59:48 -0700
Subject: [PATCH 32/51] rebase
---
.../EntityPointerLevel.h | 26 +++----
.../EntityPointerLevelFormat.h | 67 +++++++++++++++++++
.../UnsafeBufferUsage/UnsafeBufferUsage.h | 2 +-
.../Analyses/CMakeLists.txt | 2 +-
.../EntityPointerLevel.cpp | 44 +-----------
.../UnsafeBufferUsage/UnsafeBufferUsage.cpp | 33 +++------
.../UnsafeBufferUsageExtractor.cpp | 2 +-
.../tu-summary-serialization.test | 16 +++--
.../UnsafeBufferUsageTest.cpp | 2 +-
9 files changed, 103 insertions(+), 91 deletions(-)
rename clang/include/clang/ScalableStaticAnalysisFramework/Analyses/{ => EntityPointerLevel}/EntityPointerLevel.h (87%)
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
rename clang/lib/ScalableStaticAnalysisFramework/Analyses/{ => EntityPointerLevel}/EntityPointerLevel.cpp (89%)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
similarity index 87%
rename from clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
rename to clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
index 945af7d6c9f3f..752b3caaad29d 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
@@ -6,12 +6,12 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVEL_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVEL_H
#include "clang/AST/Decl.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
#include <set>
namespace clang::ssaf {
@@ -39,9 +39,6 @@ class EntityPointerLevel {
unsigned PointerLevel;
friend class EntityPointerLevelTranslator;
- friend llvm::Expected<EntityPointerLevel>
- entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
- JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
// For unittests:
friend EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
@@ -114,21 +111,14 @@ creatEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
std::function<EntityId(EntityName EN)> AddEntity,
bool IsFunRet = false);
+/// Creates a `EntityPointerLevel` from a pair of an EntityId and a pointer
+/// level:
+EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
+
/// Creates a new EntityPointerLevel (EPL) from `E` by incrementing `E`'s
/// pointer level.
/// \return the EPL that is associated with the pointee (or array element) type
/// of `E`'s associated pointer/array tyoe of the same entity.
EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E);
-
-llvm::json::Value
-entityPointerLevelToJSON(const EntityPointerLevel &EPL,
- JSONFormat::EntityIdToJSONFn EntityId2JSON);
-
-llvm::Expected<EntityPointerLevel>
-entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
- JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
-
-/// Proxy function creating EPLs for unit tests:
-EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
} // namespace clang::ssaf
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVEL_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
new file mode 100644
index 0000000000000..f8b30f8aab9d6
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
@@ -0,0 +1,67 @@
+//===- EntityPointerLevelFormat.h -------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+
+template <typename... Ts>
+static inline llvm::Error makeSawButExpectedError(const llvm::json::Value &Saw,
+ llvm::StringRef Expected,
+ const Ts &...ExpectedArgs) {
+ std::string Fmt = ("saw %s but expected " + Expected).str();
+ std::string SawStr = llvm::formatv("{0:2}", Saw).str();
+
+ return llvm::createStringError(Fmt.c_str(), SawStr.c_str(), ExpectedArgs...);
+}
+
+namespace clang::ssaf {
+// Writes an EntityPointerLevel as
+// Array [
+// Object { "@" : [entity-id]},
+// [pointer-level-integer]
+// ]
+static inline llvm::json::Value
+entityPointerLevelToJSON(const EntityPointerLevel &EPL,
+ JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+ return llvm::json::Array{EntityId2JSON(EPL.getEntity()),
+ llvm::json::Value(EPL.getPointerLevel())};
+}
+
+Expected<EntityPointerLevel> static inline entityPointerLevelFromJSON(
+ const llvm::json::Value &EPLData,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
+ auto *AsArr = EPLData.getAsArray();
+
+ if (!AsArr || AsArr->size() != 2)
+ return makeSawButExpectedError(
+ EPLData, "an array with exactly two elements representing "
+ "EntityId and PointerLevel, respectively");
+
+ auto *EntityIdObj = (*AsArr)[0].getAsObject();
+
+ if (!EntityIdObj)
+ return makeSawButExpectedError((*AsArr)[0],
+ "an object representing EntityId");
+
+ Expected<EntityId> Id = EntityIdFromJSON(*EntityIdObj);
+
+ if (!Id)
+ return Id.takeError();
+
+ std::optional<uint64_t> PtrLv = (*AsArr)[1].getAsInteger();
+
+ if (!PtrLv)
+ return makeSawButExpectedError((*AsArr)[1],
+ "an integer representing PointerLevel");
+
+ return buildEntityPointerLevel(*Id, *PtrLv);
+}
+} // namespace clang::ssaf
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
\ No newline at end of file
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
index 9b85459a284eb..27bda9f773085 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h
@@ -9,7 +9,7 @@
#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H
-#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
index 68107ce36ab99..c15ff3b3c42e7 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -5,7 +5,7 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses
CallGraph/CallGraphExtractor.cpp
CallGraph/CallGraphJSONFormat.cpp
- EntityPointerLevel.cpp
+ EntityPointerLevel/EntityPointerLevel.cpp
UnsafeBufferUsage/UnsafeBufferUsage.cpp
UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
similarity index 89%
rename from clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
rename to clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index e607529342351..b5bf17530f9f2 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -5,11 +5,12 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "llvm/Support/JSON.h"
#include <optional>
namespace {
@@ -284,47 +285,6 @@ EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
return EntityPointerLevelTranslator::incrementPointerLevel(E);
}
-// Writes an EntityPointerLevel as
-// Array [
-// Object { "@" : [entity-id]},
-// [pointer-level-integer]
-// ]
-llvm::json::Value
-entityPointerLevelToJSON(const EntityPointerLevel &EPL,
- JSONFormat::EntityIdToJSONFn EntityId2JSON) {
- return llvm::json::Array{EntityId2JSON(EPL.getEntity()),
- llvm::json::Value(EPL.getPointerLevel())};
-}
-
-Expected<EntityPointerLevel>
-entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
- JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
- auto *AsArr = EPLData.getAsArray();
-
- if (!AsArr || AsArr->size() != 2)
- return makeErrorSawButExpected(
- EPLData, "a JSON array of size 2: [EntityId, PointerLevel]");
-
- auto *EntityIdObj = (*AsArr)[0].getAsObject();
-
- if (!EntityIdObj)
- return makeErrorSawButExpected((*AsArr)[0],
- "a JSON object representing EntityID");
-
- Expected<EntityId> Id = EntityIdFromJSON(*EntityIdObj);
-
- if (!Id)
- return Id.takeError();
-
- std::optional<uint64_t> PtrLv = (*AsArr)[1].getAsInteger();
-
- if (!PtrLv)
- return makeErrorSawButExpected((*AsArr)[1],
- "a JSON value representing an integer");
-
- return EntityPointerLevel{std::tie(*Id, *PtrLv)};
-}
-
EntityPointerLevel buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
return EntityPointerLevel({Id, PtrLv});
}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index d325e8df79c20..84afb93239ffc 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -7,8 +7,9 @@
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/JSON.h"
@@ -21,10 +22,6 @@ using Object = llvm::json::Object;
static constexpr llvm::StringLiteral SummarySerializationKey = "UnsafeBuffers";
-EntityPointerLevel ssaf::buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
- return EntityPointerLevel(Id, PtrLv);
-}
-
UnsafeBufferUsageEntitySummary
ssaf::buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers) {
return UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers));
@@ -41,8 +38,7 @@ static Object serialize(const EntitySummary &S,
Array UnsafeBuffersData;
for (const auto &EPL : getUnsafeBuffers(SS))
- UnsafeBuffersData.push_back(
- Array{Fn(EPL.getEntity()), EPL.getPointerLevel()});
+ UnsafeBuffersData.push_back(entityPointerLevelToJSON(EPL, Fn));
return Object{{SummarySerializationKey.data(), std::move(UnsafeBuffersData)}};
}
@@ -52,28 +48,19 @@ deserializeImpl(const Object &Data, JSONFormat::EntityIdFromJSONFn Fn) {
Data.getArray(SummarySerializationKey.data());
if (!UnsafeBuffersData)
- return llvm::createStringError("expected a json::Object with a key %s",
+ return makeSawButExpectedError(Object(Data),
+ "an Object with a key %s",
SummarySerializationKey.data());
EntityPointerLevelSet EPLs;
for (const auto &EltData : *UnsafeBuffersData) {
- const Array *EltDataAsArr = EltData.getAsArray();
-
- if (!EltDataAsArr || EltDataAsArr->size() != 2)
- return llvm::createStringError("expected a json::Array of size 2");
-
- const Object *IdData = (*EltDataAsArr)[0].getAsObject();
- std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
-
- if (!IdData || !PtrLvData)
- return llvm::createStringError("expected a json::Value of integer type");
-
- llvm::Expected<EntityId> Id = Fn(*IdData);
+ llvm::Expected<EntityPointerLevel> EPL =
+ entityPointerLevelFromJSON(EltData, Fn);
- if (!Id)
- return Id.takeError();
- EPLs.insert(buildEntityPointerLevel(Id.get(), *PtrLvData));
+ if (!EPL)
+ return EPL.takeError();
+ EPLs.insert(*EPL);
}
return std::make_unique<UnsafeBufferUsageEntitySummary>(
buildUnsafeBufferUsageEntitySummary(std::move(EPLs)));
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index aeab9fc8867a3..b342f9299e71b 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -10,7 +10,7 @@
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
index 6a12949f3bbb4..55357acb1db08 100644
--- a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
@@ -6,13 +6,21 @@
// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-no-key.json 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-NO-KEY
-// CHECK-NO-KEY: expected a json::Object with a key UnsafeBuffers
-
+// CHECK-NO-KEY: saw {
+// CHECK-NO-KEY: "NotUnsafeBuffers": [
+// CHECK-NO-KEY: [
+// CHECK-NO-KEY: {
+// CHECK-NO-KEY: "@": 0
+// CHECK-NO-KEY: },
+// CHECK-NO-KEY: 1
+// CHECK-NO-KEY: ]
+// CHECK-NO-KEY: ]
+// CHECK-NO-KEY: } but expected an Object with a key UnsafeBuffers
// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-element.json 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-BAD-ELEMENT
-// CHECK-BAD-ELEMENT: expected a json::Array of size 2
+// CHECK-BAD-ELEMENT: saw 42 but expected an array with exactly two elements representing EntityId and PointerLevel, respectively
// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-ptr-level.json 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-BAD-PTR-LEVEL
-// CHECK-BAD-PTR-LEVEL: expected a json::Value of integer type
+// CHECK-BAD-PTR-LEVEL: saw "not-an-integer" but expected an integer representing PointerLevel
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index f14f3caa1e016..d2c513b7c70a2 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -10,7 +10,7 @@
#include "TestFixture.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/Frontend/ASTUnit.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
>From 1cf2c6a6ef56421136af0b0e1c63b858de54f2fa Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 1 Apr 2026 23:23:41 -0700
Subject: [PATCH 33/51] clean up
---
.../EntityPointerLevelFormat.h | 44 +------
.../EntityPointerLevel/EntityPointerLevel.cpp | 109 +++++++++++-------
.../UnsafeBufferUsage/UnsafeBufferUsage.cpp | 3 +-
.../UnsafeBufferUsageExtractor.cpp | 1 +
4 files changed, 77 insertions(+), 80 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
index f8b30f8aab9d6..766e425338e96 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
@@ -22,46 +22,12 @@ static inline llvm::Error makeSawButExpectedError(const llvm::json::Value &Saw,
}
namespace clang::ssaf {
-// Writes an EntityPointerLevel as
-// Array [
-// Object { "@" : [entity-id]},
-// [pointer-level-integer]
-// ]
-static inline llvm::json::Value
+llvm::json::Value
entityPointerLevelToJSON(const EntityPointerLevel &EPL,
- JSONFormat::EntityIdToJSONFn EntityId2JSON) {
- return llvm::json::Array{EntityId2JSON(EPL.getEntity()),
- llvm::json::Value(EPL.getPointerLevel())};
-}
-
-Expected<EntityPointerLevel> static inline entityPointerLevelFromJSON(
- const llvm::json::Value &EPLData,
- JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
- auto *AsArr = EPLData.getAsArray();
-
- if (!AsArr || AsArr->size() != 2)
- return makeSawButExpectedError(
- EPLData, "an array with exactly two elements representing "
- "EntityId and PointerLevel, respectively");
-
- auto *EntityIdObj = (*AsArr)[0].getAsObject();
-
- if (!EntityIdObj)
- return makeSawButExpectedError((*AsArr)[0],
- "an object representing EntityId");
+ JSONFormat::EntityIdToJSONFn EntityId2JSON);
- Expected<EntityId> Id = EntityIdFromJSON(*EntityIdObj);
-
- if (!Id)
- return Id.takeError();
-
- std::optional<uint64_t> PtrLv = (*AsArr)[1].getAsInteger();
-
- if (!PtrLv)
- return makeSawButExpectedError((*AsArr)[1],
- "an integer representing PointerLevel");
-
- return buildEntityPointerLevel(*Id, *PtrLv);
-}
+Expected<EntityPointerLevel>
+entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
} // namespace clang::ssaf
#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
\ No newline at end of file
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index b5bf17530f9f2..e1346c2ff32c6 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -1,4 +1,4 @@
-//===----------------- EntityPointerLevel.cpp -------------------*- C++ -*-===//
+//===- EntityPointerLevel.cpp ----------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,18 +6,21 @@
//
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
-#include "llvm/Support/JSON.h"
#include <optional>
-namespace {
using namespace clang;
+using namespace ssaf;
+
template <typename NodeTy, typename... Ts>
-static inline llvm::Error strErrAtNode(ASTContext &Ctx, const NodeTy &N,
- StringRef Fmt, const Ts &...Args) {
+static inline llvm::Error makeErrAtNode(ASTContext &Ctx, const NodeTy &N,
+ StringRef Fmt, const Ts &...Args) {
std::string LocStr = N.getBeginLoc().printToString(Ctx.getSourceManager());
llvm::SmallVector<char> FmtData;
@@ -25,10 +28,10 @@ static inline llvm::Error strErrAtNode(ASTContext &Ctx, const NodeTy &N,
return llvm::createStringError(FmtData.data(), Args..., LocStr.c_str());
}
-static inline llvm::Error entityNameErrFor(ASTContext &Ctx,
- const NamedDecl &D) {
- return strErrAtNode(Ctx, D, "failed to create entity name for %s",
- D.getNameAsString().data());
+static inline llvm::Error makeEntityNameErr(ASTContext &Ctx,
+ const NamedDecl &D) {
+ return makeErrAtNode(Ctx, D, "failed to create entity name for %s",
+ D.getNameAsString().data());
}
template <typename DeclOrExpr>
@@ -37,18 +40,6 @@ static bool hasPtrOrArrType(const DeclOrExpr &E) {
llvm::isa<ArrayType>(E.getType().getCanonicalType());
}
-template <typename... Ts>
-static inline llvm::Error makeErrorSawButExpected(const llvm::json::Value &Saw,
- llvm::StringRef Expected,
- const Ts &...ExpectedArgs) {
- return llvm::createStringError(
- ("saw %s but expected " + Expected).str().c_str(),
- llvm::formatv("{0:2}", Saw).str().data(), Expected.data(),
- ExpectedArgs...);
-}
-} // namespace
-
-namespace clang::ssaf {
// Translate a pointer type expression 'E' to a (set of) EntityPointerLevel(s)
// associated with the declared type of the base address of `E`. If the base
// address of `E` is not associated with an entity, the translation result is an
@@ -68,16 +59,16 @@ namespace clang::ssaf {
// Translate(arr[5]) -> {(arr, 2)}
// Translate(cond ? p1[5] : p2) -> {(p1, 2), (p2, 1)}
// Translate(&arr[5]) -> {(arr, 1)}
-class EntityPointerLevelTranslator
+class clang::ssaf::EntityPointerLevelTranslator
: public ConstStmtVisitor<EntityPointerLevelTranslator,
Expected<EntityPointerLevelSet>> {
friend class StmtVisitorBase;
// Fallback method for all unsupported expression kind:
llvm::Error fallback(const Stmt *E) {
- return strErrAtNode(Ctx, *E,
- "attempt to translate %s to EntityPointerLevels",
- E->getStmtClassName());
+ return makeErrAtNode(Ctx, *E,
+ "attempt to translate %s to EntityPointerLevels",
+ E->getStmtClassName());
}
EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) {
@@ -108,7 +99,7 @@ class EntityPointerLevelTranslator
Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
Expected<EntityPointerLevel> translate(const NamedDecl *D, bool IsRet) {
if (IsRet && !isa<FunctionDecl>(D))
- return strErrAtNode(
+ return makeErrAtNode(
Ctx, *D,
"attempt to call getEntityNameForReturn on a NamedDecl of %s kind",
D->getDeclKindName());
@@ -118,7 +109,7 @@ class EntityPointerLevelTranslator
: getEntityName(D);
if (EN)
return createEntityPointerLevelFor(*EN);
- return entityNameErrFor(Ctx, *D);
+ return makeEntityNameErr(Ctx, *D);
}
static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
@@ -241,14 +232,14 @@ class EntityPointerLevelTranslator
Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
if (auto EntityName = getEntityName(E->getDecl()))
return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
- return entityNameErrFor(Ctx, *E->getDecl());
+ return makeEntityNameErr(Ctx, *E->getDecl());
}
// Translate({., ->}f) -> {(MemberDecl, 1)}
Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
if (auto EntityName = getEntityName(E->getMemberDecl()))
return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
- return entityNameErrFor(Ctx, *E->getMemberDecl());
+ return makeEntityNameErr(Ctx, *E->getMemberDecl());
}
// Translate(`DefaultArg`) -> Translate(`DefaultArg->getExpr()`)
@@ -263,30 +254,70 @@ class EntityPointerLevelTranslator
}
};
-Expected<EntityPointerLevelSet>
-translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
- std::function<EntityId(EntityName EN)> AddEntity) {
+Expected<EntityPointerLevelSet> clang::ssaf::translateEntityPointerLevel(
+ const Expr *E, ASTContext &Ctx,
+ std::function<EntityId(EntityName EN)> AddEntity) {
EntityPointerLevelTranslator Translator(AddEntity, Ctx);
return Translator.translate(E);
}
/// Create an EntityPointerLevel from a ValueDecl of a pointer type.
-Expected<EntityPointerLevel>
-creatEntityPointerLevel(const NamedDecl *D, ASTContext &Ctx,
- std::function<EntityId(EntityName EN)> AddEntity,
- bool IsFunRet) {
+Expected<EntityPointerLevel> clang::ssaf::creatEntityPointerLevel(
+ const NamedDecl *D, ASTContext &Ctx,
+ std::function<EntityId(EntityName EN)> AddEntity, bool IsFunRet) {
EntityPointerLevelTranslator Translator(AddEntity, Ctx);
return Translator.translate(D, IsFunRet);
}
-EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
+EntityPointerLevel
+clang::ssaf::incrementPointerLevel(const EntityPointerLevel &E) {
return EntityPointerLevelTranslator::incrementPointerLevel(E);
}
-EntityPointerLevel buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
+EntityPointerLevel clang::ssaf::buildEntityPointerLevel(EntityId Id,
+ unsigned PtrLv) {
return EntityPointerLevel({Id, PtrLv});
}
-} // namespace clang::ssaf
+// Writes an EntityPointerLevel as
+// Array [
+// Object { "@" : [entity-id]},
+// [pointer-level-integer]
+// ]
+llvm::json::Value clang::ssaf::entityPointerLevelToJSON(
+ const EntityPointerLevel &EPL, JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+ return llvm::json::Array{EntityId2JSON(EPL.getEntity()),
+ llvm::json::Value(EPL.getPointerLevel())};
+}
+
+Expected<EntityPointerLevel> clang::ssaf::entityPointerLevelFromJSON(
+ const llvm::json::Value &EPLData,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
+ auto *AsArr = EPLData.getAsArray();
+
+ if (!AsArr || AsArr->size() != 2)
+ return makeSawButExpectedError(
+ EPLData, "an array with exactly two elements representing "
+ "EntityId and PointerLevel, respectively");
+
+ auto *EntityIdObj = (*AsArr)[0].getAsObject();
+
+ if (!EntityIdObj)
+ return makeSawButExpectedError((*AsArr)[0],
+ "an object representing EntityId");
+
+ Expected<EntityId> Id = EntityIdFromJSON(*EntityIdObj);
+
+ if (!Id)
+ return Id.takeError();
+
+ std::optional<uint64_t> PtrLv = (*AsArr)[1].getAsInteger();
+
+ if (!PtrLv)
+ return makeSawButExpectedError((*AsArr)[1],
+ "an integer representing PointerLevel");
+
+ return buildEntityPointerLevel(*Id, *PtrLv);
+}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 84afb93239ffc..47e03476d1063 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -48,8 +48,7 @@ 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(Object(Data), "an Object with a key %s",
SummarySerializationKey.data());
EntityPointerLevelSet EPLs;
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index b342f9299e71b..c412855028efb 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -9,6 +9,7 @@
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
>From 4c681abfe034e3bea003fb510b97f31dbf85e2b2 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 2 Apr 2026 16:14:44 -0700
Subject: [PATCH 34/51] fix issues after rebase
---
.../Analyses/EntityPointerLevel.cpp | 299 ------------------
1 file changed, 299 deletions(-)
delete mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
deleted file mode 100644
index ddbc45301ac4b..0000000000000
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.cpp
+++ /dev/null
@@ -1,299 +0,0 @@
-//===----------------- EntityPointerLevel.cpp -------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
-#include "SSAFAnalysesCommon.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclCXX.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
-#include <optional>
-
-namespace clang::ssaf {
-// Translate a pointer type expression 'E' to a (set of) EntityPointerLevel(s)
-// associated with the declared type of the base address of `E`. If the base
-// address of `E` is not associated with an entity, the translation result is an
-// empty set.
-//
-// The translation is a process of traversing into the pointer 'E' until its
-// base address can be represented by an entity, with the number of dereferences
-// tracked by incrementing the pointer level. Naturally, taking address of, as
-// the inverse operation of dereference, is tracked by decrementing the pointer
-// level.
-//
-// For example, suppose there are pointers and arrays declared as
-// int *ptr, **p1, **p2;
-// int arr[10][10];
-// , the translation of expressions involving these base addresses will be:
-// Translate(ptr + 5) -> {(ptr, 1)}
-// Translate(arr[5]) -> {(arr, 2)}
-// Translate(cond ? p1[5] : p2) -> {(p1, 2), (p2, 1)}
-// Translate(&arr[5]) -> {(arr, 1)}
-class EntityPointerLevelTranslator
- : public ConstStmtVisitor<EntityPointerLevelTranslator,
- Expected<EntityPointerLevelSet>> {
- friend class StmtVisitorBase;
-
- // Fallback method for all unsupported expression kind:
- llvm::Error fallback(const Stmt *E) {
- return strErrAtNode(Ctx, *E,
- "attempt to translate %s to EntityPointerLevels",
- E->getStmtClassName());
- }
-
- EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) {
- return EntityPointerLevel({AddEntity(Name), 1});
- }
-
- // The common helper function for Translate(*base):
- // Translate(*base) -> Translate(base) with .pointerLevel + 1
- Expected<EntityPointerLevelSet> translateDereferencePointer(const Expr *Ptr) {
- assert(hasPtrOrArrType(*Ptr));
-
- Expected<EntityPointerLevelSet> SubResult = Visit(Ptr);
- if (!SubResult)
- return SubResult.takeError();
-
- auto Incremented = llvm::map_range(*SubResult, incrementPointerLevel);
- return EntityPointerLevelSet{Incremented.begin(), Incremented.end()};
- }
-
- std::function<EntityId(EntityName EN)> AddEntity;
- ASTContext &Ctx;
-
-public:
- EntityPointerLevelTranslator(std::function<EntityId(EntityName EN)> AddEntity,
- ASTContext &Ctx)
- : AddEntity(AddEntity), Ctx(Ctx) {}
-
- Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
- Expected<EntityPointerLevel> translate(const NamedDecl *D, bool IsRet) {
- if (IsRet && !isa<FunctionDecl>(D))
- return strErrAtNode(
- Ctx, *D,
- "attempt to call getEntityNameForReturn on a NamedDecl of %s kind",
- D->getDeclKindName());
-
- std::optional<EntityName> EN =
- IsRet ? getEntityNameForReturn(cast<FunctionDecl>(D))
- : getEntityName(D);
- if (EN)
- return createEntityPointerLevelFor(*EN);
- return entityNameErrFor(Ctx, *D);
- }
-
- static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
- return EntityPointerLevel({E.getEntity(), E.getPointerLevel() + 1});
- }
-
- static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) {
- assert(E.getPointerLevel() > 0);
- return EntityPointerLevel({E.getEntity(), E.getPointerLevel() - 1});
- }
-
-private:
- Expected<EntityPointerLevelSet> VisitStmt(const Stmt *E) {
- return fallback(E);
- }
-
- // Translate(base + x) -> Translate(base)
- // Translate(x + base) -> Translate(base)
- // Translate(base - x) -> Translate(base)
- // Translate(base {+=, -=, =} x) -> Translate(base)
- // Translate(x, base) -> Translate(base)
- Expected<EntityPointerLevelSet> VisitBinaryOperator(const BinaryOperator *E) {
- switch (E->getOpcode()) {
- case clang::BO_Add:
- if (hasPtrOrArrType(*E->getLHS()))
- return Visit(E->getLHS());
- return Visit(E->getRHS());
- case clang::BO_Sub:
- case clang::BO_AddAssign:
- case clang::BO_SubAssign:
- case clang::BO_Assign:
- return Visit(E->getLHS());
- case clang::BO_Comma:
- return Visit(E->getRHS());
- default:
- return fallback(E);
- }
- }
-
- // Translate({++, --}base) -> Translate(base)
- // Translate(base{++, --}) -> Translate(base)
- // Translate(*base) -> Translate(base) with .pointerLevel += 1
- // Translate(&base) -> {}, if Translate(base) is {}
- // -> Translate(base) with .pointerLevel -= 1
- Expected<EntityPointerLevelSet> VisitUnaryOperator(const UnaryOperator *E) {
- switch (E->getOpcode()) {
- case clang::UO_PostInc:
- case clang::UO_PostDec:
- case clang::UO_PreInc:
- case clang::UO_PreDec:
- return Visit(E->getSubExpr());
- case clang::UO_AddrOf: {
- Expected<EntityPointerLevelSet> SubResult = Visit(E->getSubExpr());
- if (!SubResult)
- return SubResult.takeError();
-
- auto Decremented = llvm::map_range(*SubResult, decrementPointerLevel);
- return EntityPointerLevelSet{Decremented.begin(), Decremented.end()};
- }
- case clang::UO_Deref:
- return translateDereferencePointer(E->getSubExpr());
- default:
- return fallback(E);
- }
- }
-
- // Translate((T*)base) -> Translate(p) if p has pointer type
- // -> {} otherwise
- Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) {
- if (hasPtrOrArrType(*E->getSubExpr()))
- return Visit(E->getSubExpr());
- return EntityPointerLevelSet{};
- }
-
- // Translate(f(...)) -> {} if it is an indirect call
- // -> {(f_return, 1)}, otherwise
- Expected<EntityPointerLevelSet> VisitCallExpr(const CallExpr *E) {
- if (auto *FD = E->getDirectCallee())
- if (auto FDEntityName = getEntityNameForReturn(FD))
- return EntityPointerLevelSet{
- createEntityPointerLevelFor(*FDEntityName)};
- return EntityPointerLevelSet{};
- }
-
- // Translate(base[x]) -> Translate(*base)
- Expected<EntityPointerLevelSet>
- VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
- return translateDereferencePointer(E->getBase());
- }
-
- // Translate(cond ? base1 : base2) := Translate(base1) U Translate(base2)
- Expected<EntityPointerLevelSet>
- VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
- Expected<EntityPointerLevelSet> ReT = Visit(E->getTrueExpr());
- Expected<EntityPointerLevelSet> ReF = Visit(E->getFalseExpr());
-
- if (ReT && ReF) {
- ReT->insert(ReF->begin(), ReF->end());
- return ReT;
- }
- if (!ReF && !ReT)
- return llvm::joinErrors(ReT.takeError(), ReF.takeError());
- if (!ReF)
- return ReF.takeError();
- return ReT.takeError();
- }
-
- Expected<EntityPointerLevelSet> VisitParenExpr(const ParenExpr *E) {
- return Visit(E->getSubExpr());
- }
-
- // Translate("string-literal") -> {}
- // Buffer accesses on string literals are unsafe, but string literals are not
- // entities so there is no EntityPointerLevel associated with it.
- Expected<EntityPointerLevelSet> VisitStringLiteral(const StringLiteral *E) {
- return EntityPointerLevelSet{};
- }
-
- // Translate(DRE) -> {(Decl, 1)}
- Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
- if (auto EntityName = getEntityName(E->getDecl()))
- return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
- return entityNameErrFor(Ctx, *E->getDecl());
- }
-
- // Translate({., ->}f) -> {(MemberDecl, 1)}
- Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
- if (auto EntityName = getEntityName(E->getMemberDecl()))
- return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
- return entityNameErrFor(Ctx, *E->getMemberDecl());
- }
-
- // Translate(`DefaultArg`) -> Translate(`DefaultArg->getExpr()`)
- Expected<EntityPointerLevelSet>
- VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) {
- return Visit(E->getExpr());
- }
-
- Expected<EntityPointerLevelSet>
- VisitOpaqueValueExpr(const OpaqueValueExpr *S) {
- return Visit(S->getSourceExpr());
- }
-};
-
-Expected<EntityPointerLevelSet>
-translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
- std::function<EntityId(EntityName EN)> AddEntity) {
- EntityPointerLevelTranslator Translator(AddEntity, Ctx);
-
- return Translator.translate(E);
-}
-
-/// Create an EntityPointerLevel from a ValueDecl of a pointer type.
-Expected<EntityPointerLevel>
-creatEntityPointerLevel(const NamedDecl *D, ASTContext &Ctx,
- std::function<EntityId(EntityName EN)> AddEntity,
- bool IsFunRet) {
- EntityPointerLevelTranslator Translator(AddEntity, Ctx);
-
- return Translator.translate(D, IsFunRet);
-}
-
-EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
- return EntityPointerLevelTranslator::incrementPointerLevel(E);
-}
-
-// Writes an EntityPointerLevel as
-// Array [
-// Object { "@" : [entity-id]},
-// [pointer-level-integer]
-// ]
-llvm::json::Value
-entityPointerLevelToJSON(const EntityPointerLevel &EPL,
- JSONFormat::EntityIdToJSONFn EntityId2JSON) {
- return llvm::json::Array{EntityId2JSON(EPL.getEntity()),
- llvm::json::Value(EPL.getPointerLevel())};
-}
-
-Expected<EntityPointerLevel>
-entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
- JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
- auto *AsArr = EPLData.getAsArray();
-
- if (!AsArr || AsArr->size() != 2)
- return makeErrorSawButExpected(
- EPLData, "a JSON array of size 2: [EntityId, PointerLevel]");
-
- auto *EntityIdObj = (*AsArr)[0].getAsObject();
-
- if (!EntityIdObj)
- return makeErrorSawButExpected((*AsArr)[0],
- "a JSON object representing EntityID");
-
- Expected<EntityId> Id = EntityIdFromJSON(*EntityIdObj);
-
- if (!Id)
- return Id.takeError();
-
- std::optional<uint64_t> PtrLv = (*AsArr)[1].getAsInteger();
-
- if (!PtrLv)
- return makeErrorSawButExpected((*AsArr)[1],
- "a JSON value representing an integer");
-
- return EntityPointerLevel{std::tie(*Id, *PtrLv)};
-}
-
-EntityPointerLevel buildEntityPointerLevel(EntityId Id, unsigned PtrLv) {
- return EntityPointerLevel({Id, PtrLv});
-}
-
-} // namespace clang::ssaf
>From 036f9b1ce819e9a1e4d490562dc6e102d2a428e1 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Sat, 4 Apr 2026 23:39:25 -0700
Subject: [PATCH 35/51] remove proxy functions for unit-testing
UnsafeBufferUsageExtractor
---
.../EntityPointerLevelFormat.h | 10 -
.../EntityPointerLevel/EntityPointerLevel.cpp | 23 +-
.../Analyses/SSAFAnalysesCommon.h | 36 +-
.../UnsafeBufferUsage/UnsafeBufferUsage.cpp | 6 +-
.../UnsafeBufferUsageExtractor.cpp | 107 +-----
.../UnsafeBufferUsageTest.cpp | 337 ++++++++++--------
6 files changed, 223 insertions(+), 296 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
index 766e425338e96..a3914460c86ef 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
@@ -11,16 +11,6 @@
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
-template <typename... Ts>
-static inline llvm::Error makeSawButExpectedError(const llvm::json::Value &Saw,
- llvm::StringRef Expected,
- const Ts &...ExpectedArgs) {
- std::string Fmt = ("saw %s but expected " + Expected).str();
- std::string SawStr = llvm::formatv("{0:2}", Saw).str();
-
- return llvm::createStringError(Fmt.c_str(), SawStr.c_str(), ExpectedArgs...);
-}
-
namespace clang::ssaf {
llvm::json::Value
entityPointerLevelToJSON(const EntityPointerLevel &EPL,
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index e1346c2ff32c6..9a57a843b23eb 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include "SSAFAnalysesCommon.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
@@ -18,28 +19,6 @@
using namespace clang;
using namespace ssaf;
-template <typename NodeTy, typename... Ts>
-static inline llvm::Error makeErrAtNode(ASTContext &Ctx, const NodeTy &N,
- StringRef Fmt, const Ts &...Args) {
- std::string LocStr = N.getBeginLoc().printToString(Ctx.getSourceManager());
- llvm::SmallVector<char> FmtData;
-
- (Fmt + " at %s").toStringRef(FmtData);
- return llvm::createStringError(FmtData.data(), Args..., LocStr.c_str());
-}
-
-static inline llvm::Error makeEntityNameErr(ASTContext &Ctx,
- const NamedDecl &D) {
- return makeErrAtNode(Ctx, D, "failed to create entity name for %s",
- D.getNameAsString().data());
-}
-
-template <typename DeclOrExpr>
-static bool hasPtrOrArrType(const DeclOrExpr &E) {
- return llvm::isa<PointerType>(E.getType().getCanonicalType()) ||
- llvm::isa<ArrayType>(E.getType().getCanonicalType());
-}
-
// Translate a pointer type expression 'E' to a (set of) EntityPointerLevel(s)
// associated with the declared type of the base address of `E`. If the base
// address of `E` is not associated with an entity, the translation result is an
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
index 836fa33d1d8e1..07d6e5848f4e2 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -1,4 +1,4 @@
-//===------------------ SSAFAnalysesCommon.h --------------------*- C++ -*-===//
+//===-- SSAFAnalysesCommon.h ------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -12,9 +12,9 @@
#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
+#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
-#include "clang/AST/ParentMapContext.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Twine.h"
@@ -24,8 +24,8 @@ namespace {
using namespace clang;
template <typename NodeTy, typename... Ts>
-static inline llvm::Error strErrAtNode(ASTContext &Ctx, const NodeTy &N,
- StringRef Fmt, const Ts &...Args) {
+static inline llvm::Error makeErrAtNode(ASTContext &Ctx, const NodeTy &N,
+ StringRef Fmt, const Ts &...Args) {
std::string LocStr = N.getBeginLoc().printToString(Ctx.getSourceManager());
llvm::SmallVector<char> FmtData;
@@ -33,14 +33,14 @@ static inline llvm::Error strErrAtNode(ASTContext &Ctx, const NodeTy &N,
return llvm::createStringError(FmtData.data(), Args..., LocStr.c_str());
}
-static inline llvm::Error entityNameErrFor(ASTContext &Ctx,
- const NamedDecl &D) {
- return strErrAtNode(Ctx, D, "failed to create entity name for %s",
- D.getNameAsString().data());
+static inline llvm::Error makeEntityNameErr(ASTContext &Ctx,
+ const NamedDecl &D) {
+ return makeErrAtNode(Ctx, D, "failed to create entity name for %s",
+ D.getNameAsString().data());
}
-static inline llvm::Error failedToAddEntitySummaryFor(ASTContext &Ctx,
- const NamedDecl *D) {
+static inline llvm::Error makeAddEntitySummaryErr(ASTContext &Ctx,
+ const NamedDecl *D) {
std::string LocStr = D->getBeginLoc().printToString(Ctx.getSourceManager());
return llvm::createStringError("failed to add entity summary for %s at %s",
@@ -48,13 +48,13 @@ static inline llvm::Error failedToAddEntitySummaryFor(ASTContext &Ctx,
}
template <typename... Ts>
-static inline llvm::Error makeErrorSawButExpected(const llvm::json::Value &Saw,
+static inline llvm::Error makeSawButExpectedError(const llvm::json::Value &Saw,
llvm::StringRef Expected,
const Ts &...ExpectedArgs) {
- return llvm::createStringError(
- ("saw %s but expected " + Expected).str().c_str(),
- llvm::formatv("{0:2}", Saw).str().data(), Expected.data(),
- ExpectedArgs...);
+ std::string Fmt = ("saw %s but expected " + Expected).str();
+ std::string SawStr = llvm::formatv("{0:2}", Saw).str();
+
+ return llvm::createStringError(Fmt.c_str(), SawStr.c_str(), ExpectedArgs...);
}
template <typename DeclOrExpr>
@@ -83,12 +83,12 @@ class ContributorFinder : public DynamicRecursiveASTVisitor {
if (DC->isFileContext() || DC->isNamespace())
Contributors.push_back(D);
- return false;
+ return true;
}
};
-/// An AST visitor that skips callable decl and record decl strict-descendant
-/// because those are separate contributors.
+/// An AST visitor that skips the root node's strict-descendants that are
+/// callable Decls and record Decls, because those are separate contributors.
///
/// The visitor calls
/// `MatcherTy::matchFact(DynTypedNode &, ASTContext &, const NamedDecl
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 0e893d35e8eec..330664a52c863 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -54,12 +54,12 @@ deserializeImpl(const Object &Data, JSONFormat::EntityIdFromJSONFn Fn) {
EntityPointerLevelSet EPLs;
- for (auto &UnsafeBufferData : *AsArr) {
- auto EPL = entityPointerLevelFromJSON(UnsafeBufferData, EntityIdFromJSON);
+ for (auto &EPLData : *UnsafeBuffersData) {
+ auto EPL = entityPointerLevelFromJSON(EPLData, Fn);
if (!EPL)
return EPL.takeError();
- UnsafeBuffers.insert(*EPL);
+ EPLs.insert(*EPL);
}
return std::make_unique<UnsafeBufferUsageEntitySummary>(
buildUnsafeBufferUsageEntitySummary(std::move(EPLs)));
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index 430a7d9c7ae9d..044ea8bc47757 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -16,11 +16,16 @@
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
-namespace {
using namespace clang;
+using namespace ssaf;
+namespace {
+
struct UnsafePointerMatcher {
std::set<const Expr *> UnsafePointers;
@@ -38,59 +43,10 @@ void findFactsInContributor(const NamedDecl *Contributor, ASTContext &Ctx,
Finder.findMatches(Contributor);
UnsafePointers.merge(Matcher.UnsafePointers);
}
-
-static llvm::Error makeCreateEntityNameError(const NamedDecl *FailedDecl,
- ASTContext &Ctx) {
- std::string LocStr = FailedDecl->getSourceRange().getBegin().printToString(
- Ctx.getSourceManager());
- return llvm::createStringError(
- "failed to create entity name for %s declared at %s",
- FailedDecl->getNameAsString().c_str(), LocStr.c_str());
-}
-
-static llvm::Error makeAddEntitySummaryError(const NamedDecl *FailedContributor,
- ASTContext &Ctx) {
- std::string LocStr =
- FailedContributor->getSourceRange().getBegin().printToString(
- Ctx.getSourceManager());
- return llvm::createStringError(
- "failed to add entity summary for contributor %s declared at %s",
- FailedContributor->getNameAsString().c_str(), LocStr.c_str());
-}
-
-Expected<EntityPointerLevelSet>
-buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
- UnsafeBufferUsageTUSummaryExtractor &Extractor,
- ASTContext &Ctx,
- std::function<EntityId(EntityName)> AddEntity) {
- EntityPointerLevelSet Result{};
- llvm::Error AllErrors = llvm::ErrorSuccess();
-
- for (const Expr *Ptr : UnsafePointers) {
- Expected<EntityPointerLevelSet> Translation =
- translateEntityPointerLevel(Ptr, Ctx, AddEntity);
-
- if (Translation) {
- // Filter out those temporary invalid EntityPointerLevels associated with
- // `&E` pointers:
- auto FilteredTranslation = llvm::make_filter_range(
- *Translation, [](const EntityPointerLevel &E) -> bool {
- return E.getPointerLevel() > 0;
- });
- Result.insert(FilteredTranslation.begin(), FilteredTranslation.end());
- continue;
- }
- AllErrors = llvm::joinErrors(std::move(AllErrors), Translation.takeError());
- }
- if (AllErrors)
- return AllErrors;
- return Result;
-}
} // namespace
-namespace clang::ssaf {
-
-class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor {
+class clang::ssaf::UnsafeBufferUsageTUSummaryExtractor
+ : public TUSummaryExtractor {
public:
UnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder)
: TUSummaryExtractor(Builder) {}
@@ -127,68 +83,39 @@ class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor {
}
void HandleTranslationUnit(ASTContext &Ctx) override {
- llvm::Error Errors = llvm::ErrorSuccess();
- auto addError = [&Errors](llvm::Error Err) {
- Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
- };
ContributorFinder ContributorFinder;
- ContributorFinder.VisitTranslationUnitDecl(Ctx.getTranslationUnitDecl());
+ ContributorFinder.TraverseAST(Ctx);
for (auto *CD : ContributorFinder.Contributors) {
auto EntitySummary = extractEntitySummary(CD, Ctx);
if (!EntitySummary) {
- addError(EntitySummary.takeError());
+ llvm::report_fatal_error(EntitySummary.takeError());
continue;
}
- assert(*EntitySummary &&
- "std::unique_ptr<EntitySummary> should not be null");
+ assert(*EntitySummary);
if ((*EntitySummary)->empty())
continue;
auto ContributorName = getEntityName(CD);
if (!ContributorName) {
- addError(entityNameErrFor(Ctx, *CD));
+ llvm::report_fatal_error(makeEntityNameErr(Ctx, *CD));
continue;
}
- auto [EntitySummaryPtr, Success] = SummaryBuilder.addSummary(
+ auto [Ignored, InsertionSucceeded] = SummaryBuilder.addSummary(
addEntity(*ContributorName), std::move(*EntitySummary));
- if (!Success)
- addError(failedToAddEntitySummaryFor(Ctx, CD));
+ assert(InsertionSucceeded && "duplicated contributor extraction");
}
- // FIXME: handle errors!
- llvm::consumeError(std::move(Errors));
}
};
-// Proxy functions for unit tests:
-extern Expected<std::unique_ptr<UnsafeBufferUsageEntitySummary>>
-extractEntitySummary(UnsafeBufferUsageTUSummaryExtractor &Extractor,
- const NamedDecl *Contributor, ASTContext &Ctx) {
- return Extractor.extractEntitySummary(Contributor, Ctx);
-}
-
-extern UnsafeBufferUsageTUSummaryExtractor *
-createUnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder) {
- return new UnsafeBufferUsageTUSummaryExtractor(Builder);
-}
-
-extern void destroyUnsafeBufferUsageTUSummaryExtractor(
- UnsafeBufferUsageTUSummaryExtractor *Extractor) {
- delete Extractor;
-}
-
-extern EntityId addEntity(UnsafeBufferUsageTUSummaryExtractor &Extractor,
- const EntityName &EN) {
- return Extractor.addEntity(EN);
-}
-} // namespace clang::ssaf
-
+// NOLINTNEXTLINE(misc-use-internal-linkage)
volatile int UnsafeBufferUsageTUSummaryExtractorAnchorSource = 0;
+
static clang::ssaf::TUSummaryExtractorRegistry::Add<
ssaf::UnsafeBufferUsageTUSummaryExtractor>
- RegisterExtractor("UnsafeBufferUsageTUSummaryExtractor",
+ RegisterExtractor(UnsafeBufferUsageEntitySummary::Name,
"The TUSummaryExtractor for unsafe buffer pointers");
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 5d2dc348e30e6..627bc88ea524b 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -6,28 +6,29 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage.h"
-#include "clang/AST/DynamicRecursiveASTVisitor.h"
-#include "clang/Frontend/ASTUnit.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
#include "TestFixture.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Error.h"
#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include <cstddef>
+#include <initializer_list>
#include <memory>
#include <optional>
@@ -35,24 +36,6 @@ using namespace clang;
using namespace ssaf;
using testing::UnorderedElementsAre;
-namespace clang::ssaf {
-// Proxy functions
-class UnsafeBufferUsageTUSummaryExtractor;
-
-extern Expected<std::unique_ptr<UnsafeBufferUsageEntitySummary>>
-extractEntitySummary(UnsafeBufferUsageTUSummaryExtractor &Extractor,
- const NamedDecl *Contributor, ASTContext &Ctx);
-
-extern UnsafeBufferUsageTUSummaryExtractor *
-createUnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder);
-
-extern void destroyUnsafeBufferUsageTUSummaryExtractor(
- UnsafeBufferUsageTUSummaryExtractor *Extractor);
-
-extern EntityId addEntity(UnsafeBufferUsageTUSummaryExtractor &Extractor,
- const EntityName &EN);
-} // namespace clang::ssaf
-
namespace {
template <typename SomeDecl = NamedDecl>
const SomeDecl *findDeclByName(StringRef Name, ASTContext &Ctx) {
@@ -88,57 +71,82 @@ class UnsafeBufferUsageTest : public TestFixture {
protected:
TUSummary TUSum;
TUSummaryBuilder Builder;
- UnsafeBufferUsageTUSummaryExtractor *Extractor;
+ std::unique_ptr<TUSummaryExtractor> Extractor;
std::unique_ptr<ASTUnit> AST;
UnsafeBufferUsageTest()
: TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
- Builder(TUSum),
- Extractor(createUnsafeBufferUsageTUSummaryExtractor(Builder)) {}
+ Builder(TUSum), Extractor(nullptr) {}
- ~UnsafeBufferUsageTest() {
- destroyUnsafeBufferUsageTUSummaryExtractor(Extractor);
- }
-
- template <typename ContributorDecl = NamedDecl>
- std::unique_ptr<UnsafeBufferUsageEntitySummary>
- setUpTest(StringRef Code, StringRef ContributorName) {
+ bool setUpTest(StringRef Code) {
AST = tooling::buildASTFromCodeWithArgs(
Code, {"-Wno-unused-value", "-Wno-int-to-pointer-cast"});
- auto *ContributorDefn =
- findDeclByName<ContributorDecl>(ContributorName, AST->getASTContext());
+ for (auto &E : clang::ssaf::TUSummaryExtractorRegistry::entries()) {
+ if (E.getName() == UnsafeBufferUsageEntitySummary::Name) {
+ Extractor = E.instantiate(Builder);
+ break;
+ }
+ }
+
+ if (!Extractor) {
+ ADD_FAILURE() << "failed to find UnsafeBufferUsageTUSummaryExtractor";
+ return false;
+ }
+ Extractor->HandleTranslationUnit(AST->getASTContext());
+ return true;
+ }
- if (!ContributorDefn)
+ template <typename ContributorDecl = NamedDecl>
+ const UnsafeBufferUsageEntitySummary *
+ getEntitySummary(StringRef ContributoEntityrName) {
+ auto *ContributorDefn = findDeclByName<ContributorDecl>(
+ ContributoEntityrName, AST->getASTContext());
+
+ if (!ContributorDefn) {
+ ADD_FAILURE() << "failed to find Decl of \"" << ContributoEntityrName
+ << "\"";
return nullptr;
+ }
std::optional<EntityName> EN = getEntityName(ContributorDefn);
- if (!EN)
+ if (!EN) {
+ ADD_FAILURE() << "failed to get EntityName for contributor \""
+ << ContributoEntityrName << "\"";
return nullptr;
+ }
- auto Sum =
- extractEntitySummary(*Extractor, ContributorDefn, AST->getASTContext());
+ EntityId ContributorEntityId = Builder.addEntity(*EN);
+ auto &TUSumData = getData(TUSum);
+ auto EntitiesSumIter =
+ TUSumData.find(UnsafeBufferUsageEntitySummary::summaryName());
- if (!Sum) {
- llvm::consumeError(Sum.takeError());
+ // If none entity summary was collected, it may not be an entry in
+ // `TUSumData`:
+ if (EntitiesSumIter == TUSumData.end())
return nullptr;
- }
- assert(*Sum);
- return std::move(*Sum);
+
+ auto EntitySumIter = EntitiesSumIter->second.find(ContributorEntityId);
+
+ // If entity summary is empty, it may not exist:
+ if (EntitySumIter == EntitiesSumIter->second.end())
+ return nullptr;
+ return static_cast<const UnsafeBufferUsageEntitySummary *>(
+ EntitySumIter->second.get());
}
std::optional<EntityId> getEntityId(StringRef Name) {
if (const auto *D = findDeclByName(Name, AST->getASTContext()))
if (auto EntityName = getEntityName(D))
- return addEntity(*Extractor, *EntityName);
+ return Builder.addEntity(*EntityName);
return std::nullopt;
}
std::optional<EntityId> getEntityIdForReturn(StringRef FunName) {
if (const auto *D = findFnByName(FunName, AST->getASTContext()))
if (auto EntityName = getEntityNameForReturn(D))
- return addEntity(*Extractor, *EntityName);
+ return Builder.addEntity(*EntityName);
return std::nullopt;
}
@@ -178,8 +186,8 @@ getSubsetOf(const EntityPointerLevelSet &Set, EntityId Entity) {
}
TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
- EntityId E1 = addEntity(*Extractor, {"c:@F at foo", "", {}});
- EntityId E2 = addEntity(*Extractor, {"c:@F at bar", "", {}});
+ EntityId E1 = Builder.addEntity({"c:@F at foo", "", {}});
+ EntityId E2 = Builder.addEntity({"c:@F at bar", "", {}});
auto P1 = buildEntityPointerLevel(E1, 2);
auto P2 = buildEntityPointerLevel(E1, 2);
@@ -197,9 +205,9 @@ TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) {
}
TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
- EntityId E1 = addEntity(*Extractor, {"c:@F at foo", "", {}});
- EntityId E2 = addEntity(*Extractor, {"c:@F at bar", "", {}});
- EntityId E3 = addEntity(*Extractor, {"c:@F at baz", "", {}});
+ EntityId E1 = Builder.addEntity({"c:@F at foo", "", {}});
+ EntityId E2 = Builder.addEntity({"c:@F at bar", "", {}});
+ EntityId E3 = Builder.addEntity({"c:@F at baz", "", {}});
auto P1 = buildEntityPointerLevel(E1, 1);
auto P2 = buildEntityPointerLevel(E1, 2);
@@ -220,13 +228,14 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) {
//////////////////////////////////////////////////////////////
TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int ***p, int ****q, int x) {
p[5][5][5];
q[5][5][5][5];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U},
{"p", 2U},
@@ -266,40 +275,43 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) {
//////////////////////////////////////////////////////////////
TEST_F(UnsafeBufferUsageTest, SimpleFunctionWithUnsafePointer) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p) {
p[5];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
}
TEST_F(UnsafeBufferUsageTest, PointerArithmetic) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p, int *q) {
*(p + 5);
*(q - 3);
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
}
TEST_F(UnsafeBufferUsageTest, PointerIncrementDecrement) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p, int *q, int *r, int *s) {
(++p)[5];
(q++)[5];
(--r)[5];
(s--)[5];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum,
@@ -307,40 +319,43 @@ TEST_F(UnsafeBufferUsageTest, PointerIncrementDecrement) {
}
TEST_F(UnsafeBufferUsageTest, PointerAssignment) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p, int *q) {
(p = q + 5)[5];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
}
TEST_F(UnsafeBufferUsageTest, CompoundAssignment) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p, int *q) {
(p += 5)[5];
(q -= 3)[5];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
}
TEST_F(UnsafeBufferUsageTest, MultiLevelPointer) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int **p, int **q, int **r) {
(*p)[5];
*(*q);
*(q[5]);
r[5][5];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum,
@@ -348,13 +363,14 @@ TEST_F(UnsafeBufferUsageTest, MultiLevelPointer) {
}
TEST_F(UnsafeBufferUsageTest, ConditionalOperator) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int **p, int **q, int cond) {
(cond ? *p : *q)[5];
cond ? p[5] : q[5];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum,
@@ -362,78 +378,84 @@ TEST_F(UnsafeBufferUsageTest, ConditionalOperator) {
}
TEST_F(UnsafeBufferUsageTest, CastExpression) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(void *p, int q) {
((int*)p)[5];
((int*)q)[5];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
}
TEST_F(UnsafeBufferUsageTest, CommaOperator) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p, int x) {
(x++, p)[5];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
}
TEST_F(UnsafeBufferUsageTest, CommaOperator2) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int **p, int **q, int x) {
(p[x] = 0, q[x] = 0)[5];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}, {"q", 2U}}));
}
TEST_F(UnsafeBufferUsageTest, ParenthesizedExpression) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p) {
(((p)))[5];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
}
TEST_F(UnsafeBufferUsageTest, ArrayParameter) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int arr[], int arr2[][10]) {
int n = 5;
arr[100];
arr2[5][n];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"arr", 1U}, {"arr2", 1U}, {"arr2", 2U}}));
}
TEST_F(UnsafeBufferUsageTest, FunctionCall) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
int ** (*fp)();
int ** foo() {
fp = &foo;
foo()[5];
(*fp())[5];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
// No (foo, 2) becasue indirect calls are ignored.
@@ -441,7 +463,7 @@ TEST_F(UnsafeBufferUsageTest, FunctionCall) {
}
TEST_F(UnsafeBufferUsageTest, StructMemberAccess) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct S {
int *ptr;
int (*ptr_to_arr)[10];
@@ -451,206 +473,215 @@ TEST_F(UnsafeBufferUsageTest, StructMemberAccess) {
obj.ptr[5];
(*obj.ptr_to_arr)[n];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}, {"ptr_to_arr", 2U}}));
}
TEST_F(UnsafeBufferUsageTest, StringLiteralSubscript) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo() {
"hello"[5];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
- EXPECT_NE(Sum, nullptr);
// String literals should not generate pointer kind variables
- EXPECT_EQ(*Sum, makeSet(__LINE__, {}));
+ EXPECT_EQ(Sum, nullptr);
}
TEST_F(UnsafeBufferUsageTest, OpaqueValueExpr) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p, int *q) {
(p ?: q)[5];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}}));
}
TEST_F(UnsafeBufferUsageTest, AddressOfOperator) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int x) {
(&x)[5];
}
- )cpp",
- "foo");
-
- EXPECT_NE(Sum, nullptr);
- // Address-of should not generate pointer kind variables for 'x'
- EXPECT_EQ(*Sum, makeSet(__LINE__, {}));
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
+ // Address-of should not generate pointer kind variables for 'x':
+ EXPECT_EQ(Sum, nullptr);
}
TEST_F(UnsafeBufferUsageTest, AddressOfThenDereference) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p, int *q) {
(*(&p))[5];
(&(*q))[5];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1}, {"q", 1}}));
}
TEST_F(UnsafeBufferUsageTest, PointerToArrayOfPointers) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo() {
int * arr[10];
int * (*p)[10] = arr;
- (*p)[5][5]; // '(*p)[5]' is unsafe
+ (*p)[5][5]; // '(*p)[5]' is unsafe
// '(*p)' is fine because 5 < 10
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 3}}));
}
TEST_F(UnsafeBufferUsageTest, UnsafePointerInGlobalVariableInitializer) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
int *gp;
int x = gp[5];
- )cpp",
- {"x"});
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("x");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
}
TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
int *gp;
struct Foo {
int field = gp[5];
};
- )cpp",
- {"Foo"});
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("Foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
}
TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer2) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
int *gp;
union Foo {
int field = gp[5];
int x;
};
- )cpp",
- {"Foo"});
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("Foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
}
TEST_F(UnsafeBufferUsageTest, InitializerList) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
int *gp;
struct Foo {
int field;
int x;
};
Foo FooObj{gp[5], 0};
- )cpp",
- {"FooObj"});
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("FooObj");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
}
TEST_F(UnsafeBufferUsageTest, UnsafePointerInCXXCtorInitializer) {
- auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct Foo {
int member;
Foo(int *p) : member(p[5]) {}
};
- )cpp",
- {"Foo"});
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary<CXXConstructorDecl>("Foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}}));
}
TEST_F(UnsafeBufferUsageTest, UnsafePointerInDefaultArg) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
int * gp;
void foo(int x = gp[5]);
- )cpp",
- {"foo"});
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("foo");
EXPECT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}}));
}
TEST_F(UnsafeBufferUsageTest, NestedDefinitions) {
- StringRef Code = R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
int * a = [](){
struct Foo {
void bar(int * ptr) { ptr[3] = 0; }
};
return nullptr;
}();
- )cpp";
- auto Sum = setUpTest(Code, {"bar"});
+ )cpp"),
+ true);
+ const auto *Sum = getEntitySummary("bar");
EXPECT_NE(Sum, nullptr);
// The closest contributor owns the fact:
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}}));
- Sum = setUpTest(Code, {"Foo"});
+ Sum = getEntitySummary("Foo");
- EXPECT_NE(Sum, nullptr);
- EXPECT_TRUE(Sum->empty());
+ EXPECT_EQ(Sum, nullptr);
- Sum = setUpTest(Code, {"a"});
+ Sum = getEntitySummary("a");
- EXPECT_NE(Sum, nullptr);
- EXPECT_TRUE(Sum->empty());
+ EXPECT_EQ(Sum, nullptr);
}
TEST_F(UnsafeBufferUsageTest, NestedDefinitions2) {
- StringRef Code = R"cpp(
+ bool SetupSuccess = setUpTest(R"cpp(
int main(void) {
struct Foo {
void bar(int * ptr) { ptr[3] = 0; }
};
}
- )cpp";
- auto Sum = setUpTest(Code, {"bar"});
+ )cpp");
+
+ ASSERT_EQ(SetupSuccess, true);
+
+ const auto *Sum = getEntitySummary("bar");
EXPECT_NE(Sum, nullptr);
// The closest contributor owns the fact:
EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}}));
- Sum = setUpTest(Code, {"Foo"});
+ Sum = getEntitySummary("Foo");
- EXPECT_NE(Sum, nullptr);
- EXPECT_TRUE(Sum->empty());
+ EXPECT_EQ(Sum, nullptr);
- Sum = setUpTest(Code, {"main"});
+ Sum = getEntitySummary("main");
- EXPECT_NE(Sum, nullptr);
- EXPECT_TRUE(Sum->empty());
+ EXPECT_EQ(Sum, nullptr);
}
} // namespace
>From 1a7d533991ea7f95f5258019412e20262e83b9f0 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Sat, 4 Apr 2026 23:43:10 -0700
Subject: [PATCH 36/51] fix clang-format issue
---
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
index 330664a52c863..6bebea52a3768 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
-#include "SSAFAnalysesCommon.h"
+#include "SSAFAnalysesCommon.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
>From ae57606288acd7ef645a0526fb6a2cbee103acea Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Mon, 6 Apr 2026 14:38:34 -0700
Subject: [PATCH 37/51] fix build issue
---
.../Analyses/SSAFAnalysesCommon.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
index 07d6e5848f4e2..9d9b445420d20 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -14,6 +14,7 @@
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/DeclObjC.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/SmallVector.h"
>From 8e548903cf65f78bc4a79d6a7b788d222d14cdb6 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Mon, 6 Apr 2026 16:28:39 -0700
Subject: [PATCH 38/51] fix bugs
---
.../ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h | 2 +-
.../Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
index 746e4776e4cbf..916ef1ce783c4 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
@@ -39,7 +39,7 @@ extern volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource;
extern volatile int UnsafeBufferUsageTUSummaryExtractorAnchorSource;
[[maybe_unused]] static int
UnsafeBufferUsageTUSummaryExtractorAnchorDestination =
- UnsafeBufferUsageSSAFJSONFormatAnchorSource;
+ UnsafeBufferUsageTUSummaryExtractorAnchorSource;
// This anchor is used to force the linker to link the CallGraphExtractor.
extern volatile int CallGraphExtractorAnchorSource;
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 627bc88ea524b..879b7c9b6b316 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -537,7 +537,7 @@ TEST_F(UnsafeBufferUsageTest, PointerToArrayOfPointers) {
ASSERT_EQ(setUpTest(R"cpp(
void foo() {
int * arr[10];
- int * (*p)[10] = arr;
+ int * (*p)[10] = &arr;
(*p)[5][5]; // '(*p)[5]' is unsafe
// '(*p)' is fine because 5 < 10
>From abc7647c5726d8e979c234183fdff0fc129f603f Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Mon, 6 Apr 2026 16:59:35 -0700
Subject: [PATCH 39/51] fix typo
---
.../Analyses/EntityPointerLevel.h | 2 +-
.../Analyses/SSAFAnalysesCommon.h | 6 +++---
.../UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp | 12 ++++--------
.../UnsafeBufferUsage/UnsafeBufferUsageTest.cpp | 8 ++++----
4 files changed, 12 insertions(+), 16 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
index f2a2c73a29a7a..edc62b0521ed7 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
@@ -110,7 +110,7 @@ translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
/// \param IsFunRet true iff the created EPL is associated with the return type
/// of a function entity.
llvm::Expected<EntityPointerLevel>
-creatEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
+createEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
std::function<EntityId(EntityName EN)> AddEntity,
bool IsFunRet = false);
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
index 9d9b445420d20..861dac3af8724 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -21,7 +21,6 @@
#include "llvm/ADT/Twine.h"
#include "llvm/Support/JSON.h"
-namespace {
using namespace clang;
template <typename NodeTy, typename... Ts>
@@ -59,11 +58,12 @@ static inline llvm::Error makeSawButExpectedError(const llvm::json::Value &Saw,
}
template <typename DeclOrExpr>
-static bool hasPtrOrArrType(const DeclOrExpr &E) {
+static inline bool hasPtrOrArrType(const DeclOrExpr &E) {
return llvm::isa<PointerType>(E.getType().getCanonicalType()) ||
llvm::isa<ArrayType>(E.getType().getCanonicalType());
}
+namespace clang::ssaf {
/// Traverses the AST and finds contributors:
class ContributorFinder : public DynamicRecursiveASTVisitor {
public:
@@ -144,5 +144,5 @@ class ContributorFactFinder : public DynamicRecursiveASTVisitor {
return true; // skip lambda as it is a callable
}
};
-} // namespace
+} // namespace clang::ssaf
#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
\ No newline at end of file
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index 044ea8bc47757..e0bad1b8cb310 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -89,20 +89,16 @@ class clang::ssaf::UnsafeBufferUsageTUSummaryExtractor
for (auto *CD : ContributorFinder.Contributors) {
auto EntitySummary = extractEntitySummary(CD, Ctx);
- if (!EntitySummary) {
- llvm::report_fatal_error(EntitySummary.takeError());
- continue;
- }
+ if (!EntitySummary)
+ llvm::reportFatalInternalError(EntitySummary.takeError());
assert(*EntitySummary);
if ((*EntitySummary)->empty())
continue;
auto ContributorName = getEntityName(CD);
- if (!ContributorName) {
- llvm::report_fatal_error(makeEntityNameErr(Ctx, *CD));
- continue;
- }
+ if (!ContributorName)
+ llvm::reportFatalInternalError(makeEntityNameErr(Ctx, *CD));
auto [Ignored, InsertionSucceeded] = SummaryBuilder.addSummary(
addEntity(*ContributorName), std::move(*EntitySummary));
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
index 879b7c9b6b316..151153ea856d6 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
@@ -99,12 +99,12 @@ class UnsafeBufferUsageTest : public TestFixture {
template <typename ContributorDecl = NamedDecl>
const UnsafeBufferUsageEntitySummary *
- getEntitySummary(StringRef ContributoEntityrName) {
+ getEntitySummary(StringRef ContributorEntityName) {
auto *ContributorDefn = findDeclByName<ContributorDecl>(
- ContributoEntityrName, AST->getASTContext());
+ ContributorEntityName, AST->getASTContext());
if (!ContributorDefn) {
- ADD_FAILURE() << "failed to find Decl of \"" << ContributoEntityrName
+ ADD_FAILURE() << "failed to find Decl of \"" << ContributorEntityName
<< "\"";
return nullptr;
}
@@ -113,7 +113,7 @@ class UnsafeBufferUsageTest : public TestFixture {
if (!EN) {
ADD_FAILURE() << "failed to get EntityName for contributor \""
- << ContributoEntityrName << "\"";
+ << ContributorEntityName << "\"";
return nullptr;
}
>From 63be9b2606098b1704b74a8bec7c0904d959423e Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Mon, 6 Apr 2026 17:27:14 -0700
Subject: [PATCH 40/51] fix format
---
.../Analyses/EntityPointerLevel.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
index edc62b0521ed7..aa4123dd372af 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
@@ -111,8 +111,8 @@ translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
/// of a function entity.
llvm::Expected<EntityPointerLevel>
createEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
- std::function<EntityId(EntityName EN)> AddEntity,
- bool IsFunRet = false);
+ std::function<EntityId(EntityName EN)> AddEntity,
+ bool IsFunRet = false);
/// Creates a new EntityPointerLevel (EPL) from `E` by incrementing `E`'s
/// pointer level.
>From 8b954b935685f75679ec6a595592db2f4cb177d2 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Tue, 7 Apr 2026 17:06:18 -0700
Subject: [PATCH 41/51] rename to PointerFlow
---
.../Analyses/EntityPointerLevel.h | 134 ---------------
.../EntityPointerLevel/EntityPointerLevel.h | 2 +-
.../PointerFlow.h} | 32 ++--
.../SSAFBuiltinForceLinker.h | 17 +-
.../Analyses/CMakeLists.txt | 4 +-
.../EntityPointerLevel/EntityPointerLevel.cpp | 4 +-
.../PointerFlow.cpp} | 39 +++--
.../PointerFlowExtractor.cpp} | 66 +++----
.../UnsafeBufferUsage/Inputs/tu-summary.json | 4 +-
.../PointerFlowTest.cpp} | 161 +++++++++---------
.../CMakeLists.txt | 2 +-
11 files changed, 167 insertions(+), 298 deletions(-)
delete mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
rename clang/include/clang/ScalableStaticAnalysisFramework/Analyses/{PointerAssignments.h => PointerFlow/PointerFlow.h} (67%)
rename clang/lib/ScalableStaticAnalysisFramework/Analyses/{PointerAssignments/PointerAssignments.cpp => PointerFlow/PointerFlow.cpp} (71%)
rename clang/lib/ScalableStaticAnalysisFramework/Analyses/{PointerAssignments/PointerAssignmentsExtractor.cpp => PointerFlow/PointerFlowExtractor.cpp} (86%)
rename clang/unittests/ScalableStaticAnalysisFramework/Analyses/{PointerAssignmentsTest.cpp => PointerFlow/PointerFlowTest.cpp} (87%)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
deleted file mode 100644
index 012fdf20aa61e..0000000000000
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h
+++ /dev/null
@@ -1,134 +0,0 @@
-//===---------------- EntityPointerLevel.h ----------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
-
-#include "clang/AST/Decl.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
-#include <set>
-
-namespace clang::ssaf {
-
-/// An EntityPointerLevel is associated with a level of the declared
-/// pointer/array type of an entity. In the fully-expanded spelling of the
-/// declared type, a EntityPointerLevel is associated with a '*' (or a '[]`) in
-/// that declaration.
-///
-/// For example, for 'int *p[10];', there are two EntityPointerLevels. One
-/// is associated with 'int *[10]' of 'p' and the other is associated with 'int
-/// *' of 'p'.
-///
-/// An EntityPointerLevel can be identified by an EntityId and an unsigned
-/// integer indicating the pointer level: '(EntityId, PointerLevel)'.
-/// An EntityPointerLevel 'P' is valid iff 'P.EntityId' has a pointer type with
-/// at least 'P.PointerLevel' levels (This implies 'P.PointerLevel > 0').
-///
-/// For the same example 'int *p[10];', the EntityPointerLevels below are valid:
-/// - '(p, 2)' is associated with the 'int *' part of the declared type of 'p';
-/// - '(p, 1)' is associated with the 'int *[10]' part of the declared type of
-/// 'p'.
-class EntityPointerLevel {
- EntityId Entity;
- unsigned PointerLevel;
-
- friend class EntityPointerLevelTranslator;
- friend llvm::Expected<EntityPointerLevel>
- entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
- JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
- // For unittests:
- friend EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
-
- // EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
- // : Entity(Entity), PointerLevel(PointerLevel) {}
- EntityPointerLevel(std::pair<EntityId, unsigned> Pair)
- : Entity(Pair.first), PointerLevel(Pair.second) {}
-
-public:
- EntityId getEntity() const { return Entity; }
- unsigned getPointerLevel() const { return PointerLevel; }
-
- bool operator==(const EntityPointerLevel &Other) const {
- return std::tie(Entity, PointerLevel) ==
- std::tie(Other.Entity, Other.PointerLevel);
- }
-
- bool operator!=(const EntityPointerLevel &Other) const {
- return !(*this == Other);
- }
-
- bool operator<(const EntityPointerLevel &Other) const {
- return std::tie(Entity, PointerLevel) <
- std::tie(Other.Entity, Other.PointerLevel);
- }
-
- /// Compares `EntityPointerLevel`s; additionally, partially compares
- /// `EntityPointerLevel` with `EntityId`.
- struct Comparator {
- using is_transparent = void;
- bool operator()(const EntityPointerLevel &L,
- const EntityPointerLevel &R) const {
- return L < R;
- }
- bool operator()(const EntityId &L, const EntityPointerLevel &R) const {
- return L < R.getEntity();
- }
- bool operator()(const EntityPointerLevel &L, const EntityId &R) const {
- return L.getEntity() < R;
- }
- };
-};
-
-using EntityPointerLevelSet =
- std::set<EntityPointerLevel, EntityPointerLevel::Comparator>;
-
-/// Translate a pointer/array type expression 'E' to a (set of)
-/// EntityPointerLevel(s) associated with the declared type of the base address
-/// of `E`. If the base address of `E` is not associated with an entity, the
-/// translation result is an empty set.
-///
-/// \param E the pointer expression to be translated
-/// \param Ctx the AST context of `E`
-/// \param AddEntity the callback provided by the caller to convert EntityNames
-/// to EntityIds.
-llvm::Expected<EntityPointerLevelSet>
-translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
- std::function<EntityId(EntityName EN)> AddEntity);
-
-/// Create EntityPointerLevel(s) (EPLs) from a NamedDecl of a pointer/array type.
-///
-/// \param ND the pointer type Decl to be translated
-/// \param Ctx the AST context of `E`
-/// \param AddEntity the callback provided by the caller to convert EntityNames
-/// to EntityIds.
-/// \param IsFunRet true iff the created EPL is associated with the return type
-/// of a function entity.
-llvm::Expected<EntityPointerLevel>
-createEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
- std::function<EntityId(EntityName EN)> AddEntity,
- bool IsFunRet = false);
-
-/// Creates new EntityPointerLevel(s) (EPLs) from the provided
-/// one(s) by incrementing their pointer levels.
-/// \return the EPL(s) that is associated with the pointee (or array element)
-/// type of `E`'s associated pointer/array tyoe of the same entity.
-EntityPointerLevelSet incrementPointerLevel(const EntityPointerLevelSet &E);
-
-llvm::json::Value
-entityPointerLevelToJSON(const EntityPointerLevel &EPL,
- JSONFormat::EntityIdToJSONFn EntityId2JSON);
-
-llvm::Expected<EntityPointerLevel>
-entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
- JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
-
-/// Proxy function creating EPLs for unit tests:
-EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
-} // namespace clang::ssaf
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
index 752b3caaad29d..56ae2e4fd5f36 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
@@ -107,7 +107,7 @@ translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
/// \param IsFunRet true iff the created EPL is associated with the return type
/// of a function entity.
llvm::Expected<EntityPointerLevel>
-creatEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
+createEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
std::function<EntityId(EntityName EN)> AddEntity,
bool IsFunRet = false);
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
similarity index 67%
rename from clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h
rename to clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
index cc82f2479a47d..b95c42328ee69 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
@@ -1,4 +1,4 @@
-//===---------------- PointerAssignments.h ----------------------*- C++ -*-===//
+//===- PointerFlow.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.
@@ -11,22 +11,24 @@
// nodes.
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERASSIGNMENTS_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERASSIGNMENTS_H
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERASSIGNMENTS_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERASSIGNMENTS_H
-#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
namespace clang::ssaf {
/// Maps LHSs to their RHS sets:
using EdgeSet = std::map<EntityPointerLevel, EntityPointerLevelSet>;
-class PointerAssignmentsEntitySummary final : public EntitySummary {
+class PointerFlowEntitySummary final : public EntitySummary {
EdgeSet Edges;
- friend class PointerAssignmentsTUSummaryExtractor;
+ friend class PointerFlowTUSummaryExtractor;
- PointerAssignmentsEntitySummary(EdgeSet Edges)
+ PointerFlowEntitySummary(EdgeSet Edges)
: EntitySummary(), Edges(std::move(Edges)) {}
public:
@@ -34,7 +36,7 @@ class PointerAssignmentsEntitySummary final : public EntitySummary {
bool operator==(const EdgeSet &Other) const { return Edges == Other; }
- bool operator==(const PointerAssignmentsEntitySummary &Other) const {
+ bool operator==(const PointerFlowEntitySummary &Other) const {
return Edges == Other.Edges;
}
@@ -48,17 +50,17 @@ class PointerAssignmentsEntitySummary final : public EntitySummary {
summaryFromJSON(const llvm::json::Object &Data, EntityIdTable &,
JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
- static SummaryName summaryName() { return SummaryName{"PointerAssignments"}; }
+ static SummaryName summaryName() { return SummaryName{"PointerFlow"}; }
};
-struct PointerAssignmentsJSONFormatInfo : JSONFormat::FormatInfo {
- PointerAssignmentsJSONFormatInfo()
+struct PointerFlowJSONFormatInfo : JSONFormat::FormatInfo {
+ PointerFlowJSONFormatInfo()
: JSONFormat::FormatInfo(
- PointerAssignmentsEntitySummary::summaryName(),
- PointerAssignmentsEntitySummary::summaryToJSON,
- PointerAssignmentsEntitySummary::summaryFromJSON) {}
+ PointerFlowEntitySummary::summaryName(),
+ PointerFlowEntitySummary::summaryToJSON,
+ PointerFlowEntitySummary::summaryFromJSON) {}
};
} // namespace clang::ssaf
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERASSIGNMENTS_H
\ No newline at end of file
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERASSIGNMENTS_H
\ No newline at end of file
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
index e6af437160ed9..6b98d4428f8dd 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
@@ -45,18 +45,17 @@ extern volatile int UnsafeBufferUsageTUSummaryExtractorAnchorSource;
UnsafeBufferUsageTUSummaryExtractorAnchorDestination =
UnsafeBufferUsageTUSummaryExtractorAnchorSource;
-// This anchor is used to force the linker to link the PointerAssignments
+// This anchor is used to force the linker to link the PointerFlow
// JSONFormat registration:
-extern volatile int PointerAssignmentsSSAFJSONFormatAnchorSource;
-[[maybe_unused]] static int PointerAssignmentsSSAFJSONFormatAnchorDestination =
- PointerAssignmentsSSAFJSONFormatAnchorSource;
+extern volatile int PointerFlowSSAFJSONFormatAnchorSource;
+[[maybe_unused]] static int PointerFlowSSAFJSONFormatAnchorDestination =
+ PointerFlowSSAFJSONFormatAnchorSource;
-// This anchor is used to force the linker to link the PointerAssignments
+// This anchor is used to force the linker to link the PointerFlow
// TUSummaryExtractor registration.
-extern volatile int PointerAssignmentsTUSummaryExtractorAnchorSource;
-[[maybe_unused]] static int
- PointerAssignmentsTUSummaryExtractorAnchorDestination =
- PointerAssignmentsTUSummaryExtractorAnchorSource;
+extern volatile int PointerFlowTUSummaryExtractorAnchorSource;
+[[maybe_unused]] static int PointerFlowTUSummaryExtractorAnchorDestination =
+ PointerFlowTUSummaryExtractorAnchorSource;
// This anchor is used to force the linker to link the CallGraphExtractor.
extern volatile int CallGraphExtractorAnchorSource;
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
index 3fc281f397dba..22c4443366a61 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt
@@ -6,10 +6,10 @@ add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses
CallGraph/CallGraphExtractor.cpp
CallGraph/CallGraphJSONFormat.cpp
EntityPointerLevel/EntityPointerLevel.cpp
+ PointerFlow/PointerFlow.cpp
+ PointerFlow/PointerFlowExtractor.cpp
UnsafeBufferUsage/UnsafeBufferUsage.cpp
UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
- PointerAssignments/PointerAssignments.cpp
- PointerAssignments/PointerAssignmentsExtractor.cpp
LINK_LIBS
clangAST
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index 4151492f40fca..69aaf0a76be8c 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -244,7 +244,7 @@ Expected<EntityPointerLevelSet> clang::ssaf::translateEntityPointerLevel(
}
/// Create an EntityPointerLevel from a ValueDecl of a pointer type.
-Expected<EntityPointerLevel> clang::ssaf::creatEntityPointerLevel(
+Expected<EntityPointerLevel> clang::ssaf::createEntityPointerLevel(
const NamedDecl *D, ASTContext &Ctx,
std::function<EntityId(EntityName EN)> AddEntity, bool IsFunRet) {
EntityPointerLevelTranslator Translator(AddEntity, Ctx);
@@ -252,7 +252,7 @@ Expected<EntityPointerLevel> clang::ssaf::creatEntityPointerLevel(
if (!EPL)
return EPL.takeError();
- return EntityPointerLevelSet{*EPL};
+ return EPL;
}
EntityPointerLevel
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignments.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
similarity index 71%
rename from clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignments.cpp
rename to clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
index 8f998ec35abc2..58aea3461c9c2 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignments.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
@@ -1,4 +1,4 @@
-//===---------- PointerAssignments.cpp -----------------------------------===//
+//===- PointerFlow.cpp ---------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,8 +6,9 @@
//
//===---------------------------------------------------------------------===//
-#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h"
#include "SSAFAnalysesCommon.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
#include "llvm/ADT/STLExtras.h"
@@ -15,7 +16,7 @@
#include "llvm/Support/JSON.h"
namespace {
-constexpr const char *const PointerAssignmentsKey = "PointerAssignments";
+constexpr const char *const PointerFlowKey = "PointerFlow";
} // namespace
namespace clang::ssaf {
@@ -29,12 +30,12 @@ using Value = llvm::json::Value;
// Array [ [lhs-node], [rhs-node], [rhs-node], ...]
// ...
// ]
-llvm::json::Object PointerAssignmentsEntitySummary::summaryToJSON(
+llvm::json::Object PointerFlowEntitySummary::summaryToJSON(
const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityId2JSON) {
Array EdgesData;
for (const auto &Entry :
- static_cast<const PointerAssignmentsEntitySummary &>(ES).Edges) {
+ static_cast<const PointerFlowEntitySummary &>(ES).Edges) {
Array EdgesEntryData;
EntityPointerLevel LHS = Entry.first;
@@ -47,31 +48,31 @@ llvm::json::Object PointerAssignmentsEntitySummary::summaryToJSON(
Object Data;
- Data[PointerAssignmentsKey] = Value(std::move(EdgesData));
+ Data[PointerFlowKey] = Value(std::move(EdgesData));
return Data;
}
llvm::Expected<std::unique_ptr<EntitySummary>>
-PointerAssignmentsEntitySummary::summaryFromJSON(
+PointerFlowEntitySummary::summaryFromJSON(
const Object &Data, EntityIdTable &,
JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
- const Value *EdgesData = Data.get(PointerAssignmentsKey);
+ const Value *EdgesData = Data.get(PointerFlowKey);
if (!EdgesData)
- return makeErrorSawButExpected(
- Object(Data), "a JSON object with the key: %s", PointerAssignmentsKey);
+ return makeSawButExpectedError(
+ Object(Data), "a JSON object with the key: %s", PointerFlowKey);
EdgeSet Edges;
const auto *EdgesDataAsArr = EdgesData->getAsArray();
if (!EdgesDataAsArr)
- return makeErrorSawButExpected(
+ return makeSawButExpectedError(
*EdgesData, "a JSON array of arary of EntityPointerLevels");
for (const auto &EdgesEntryData : *EdgesDataAsArr) {
const auto *EPLArray = EdgesEntryData.getAsArray();
if (!EPLArray || EPLArray->size() <= 1)
- return makeErrorSawButExpected(
+ return makeSawButExpectedError(
EdgesEntryData, "a JSON array of EntityPointerLevels with a size "
"greater than 1: [lhs, rhs, rhs, ...]");
@@ -89,17 +90,15 @@ PointerAssignmentsEntitySummary::summaryFromJSON(
return Err;
Edges[*EPLs.begin()].insert(EPLs.begin() + 1, EPLs.end());
}
- return std::make_unique<PointerAssignmentsEntitySummary>(
- PointerAssignmentsEntitySummary(std::move(Edges)));
+ return std::make_unique<PointerFlowEntitySummary>(
+ PointerFlowEntitySummary(std::move(Edges)));
}
-static llvm::Registry<JSONFormat::FormatInfo>::Add<
- PointerAssignmentsJSONFormatInfo>
- RegisterPointerAssignmentsJSONFormatInfo(
- "PointerAssignments",
- "JSON Format info for PointerAssignmentsEntitySummary");
+static llvm::Registry<JSONFormat::FormatInfo>::Add<PointerFlowJSONFormatInfo>
+ RegisterPointerFlowJSONFormatInfo(
+ "PointerFlow", "JSON Format info for PointerFlowEntitySummary");
} // namespace clang::ssaf
// NOLINTNEXTLINE(misc-use-internal-linkage)
-volatile int PointerAssignmentsSSAFJSONFormatAnchorSource = 0;
\ No newline at end of file
+volatile int PointerFlowSSAFJSONFormatAnchorSource = 0;
\ No newline at end of file
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignmentsExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
similarity index 86%
rename from clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignmentsExtractor.cpp
rename to clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
index a49e2ffdab441..49e682a199309 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerAssignments/PointerAssignmentsExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
@@ -1,4 +1,4 @@
-//===---- PointerAssignmentsExtractor.cpp --------------------------------===//
+//===- PointerFlowExtractor.cpp -----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -15,15 +15,15 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/TypeBase.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h"
-#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h"
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
-#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Error.h"
#include <functional>
#include <memory>
@@ -40,7 +40,7 @@ class PointerAssignmentMatcher {
// Convert a Expr/NamedDecl to an EntityPointerLevel(Set):
Expected<EntityPointerLevelSet> toEPL(const NamedDecl *N,
bool IsRet = false) {
- auto Ret = creatEntityPointerLevel(N, Ctx, AddEntity, IsRet);
+ auto Ret = createEntityPointerLevel(N, Ctx, AddEntity, IsRet);
if (Ret)
return EntityPointerLevelSet{*Ret};
@@ -126,11 +126,14 @@ class PointerAssignmentMatcher {
return false;
}
- EntityPointerLevelSet LHS = *Expected;
-
- for (unsigned I = 0; I < ArrayElementIndirectLevel; ++I)
- LHS = incrementPointerLevel(LHS);
- return addEdges(LHS, InitExpr);
+ auto R = llvm::map_range(*Expected, [&ArrayElementIndirectLevel](
+ const EntityPointerLevel &EPL) {
+ EntityPointerLevel Result = EPL;
+ for (unsigned I = 0; I < ArrayElementIndirectLevel; ++I)
+ Result = incrementPointerLevel(Result);
+ return Result;
+ });
+ return addEdges(EntityPointerLevelSet{R.begin(), R.end()}, InitExpr);
}
// Note that `Base`'s type is NOT the real LHS type when
// ArrayElementIndirectLevel > 0:
@@ -150,7 +153,7 @@ class PointerAssignmentMatcher {
if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RecordTy))
if (CXXRD->getNumBases() != 0) {
// FIXME: support this:
- addError(strErrAtNode(
+ addError(makeErrAtNode(
Ctx, *ILE,
"attempt to create pointer assignment edges between "
"CXXRecordDecls with base classes and initializer-lists"));
@@ -290,16 +293,16 @@ class PointerAssignmentMatcher {
} // namespace
namespace clang::ssaf {
-class PointerAssignmentsTUSummaryExtractor : public TUSummaryExtractor {
+class PointerFlowTUSummaryExtractor : public TUSummaryExtractor {
public:
- PointerAssignmentsTUSummaryExtractor(TUSummaryBuilder &Builder)
+ PointerFlowTUSummaryExtractor(TUSummaryBuilder &Builder)
: TUSummaryExtractor(Builder) {}
EntityId addEntity(const EntityName &EN) {
return SummaryBuilder.addEntity(EN);
}
- Expected<std::unique_ptr<PointerAssignmentsEntitySummary>>
+ Expected<std::unique_ptr<PointerFlowEntitySummary>>
extractEntitySummary(const NamedDecl *Contributor, ASTContext &Ctx) {
PointerAssignmentMatcher Matcher(
Ctx, [this](const EntityName &EN) { return addEntity(EN); });
@@ -308,14 +311,14 @@ class PointerAssignmentsTUSummaryExtractor : public TUSummaryExtractor {
Finder.findMatches(const_cast<NamedDecl *>(Contributor));
if (Matcher.Error)
return std::move(Matcher.Error);
- return std::make_unique<PointerAssignmentsEntitySummary>(
- PointerAssignmentsEntitySummary(std::move(Matcher.Results)));
+ return std::make_unique<PointerFlowEntitySummary>(
+ PointerFlowEntitySummary(std::move(Matcher.Results)));
}
void HandleTranslationUnit(ASTContext &Ctx) override;
};
-void PointerAssignmentsTUSummaryExtractor::HandleTranslationUnit(
+void PointerFlowTUSummaryExtractor::HandleTranslationUnit(
ASTContext &Ctx) {
llvm::Error Errors = llvm::ErrorSuccess();
auto addError = [&Errors](llvm::Error Err) {
@@ -339,7 +342,7 @@ void PointerAssignmentsTUSummaryExtractor::HandleTranslationUnit(
auto ContributorName = getEntityName(CD);
if (!ContributorName) {
- addError(entityNameErrFor(Ctx, *CD));
+ addError(makeEntityNameErr(Ctx, *CD));
continue;
}
@@ -347,37 +350,38 @@ void PointerAssignmentsTUSummaryExtractor::HandleTranslationUnit(
addEntity(*ContributorName), std::move(*EntitySummary));
if (!Success)
- addError(failedToAddEntitySummaryFor(Ctx, CD));
+ addError(makeAddEntitySummaryErr(Ctx, CD));
}
// FIXME: handle errors!
llvm::consumeError(std::move(Errors));
}
// Proxy functions for unit test:
-extern Expected<std::unique_ptr<PointerAssignmentsEntitySummary>>
-extractEntitySummary(PointerAssignmentsTUSummaryExtractor &Extractor,
+extern Expected<std::unique_ptr<PointerFlowEntitySummary>>
+extractEntitySummary(PointerFlowTUSummaryExtractor &Extractor,
const NamedDecl *Contributor, ASTContext &Ctx) {
return Extractor.extractEntitySummary(Contributor, Ctx);
}
-extern PointerAssignmentsTUSummaryExtractor *
-createPointerAssignmentsTUSummaryExtractor(TUSummaryBuilder &Builder) {
- return new PointerAssignmentsTUSummaryExtractor(
- PointerAssignmentsTUSummaryExtractor(Builder));
+extern PointerFlowTUSummaryExtractor *
+createPointerFlowTUSummaryExtractor(TUSummaryBuilder &Builder) {
+ return new PointerFlowTUSummaryExtractor(
+ PointerFlowTUSummaryExtractor(Builder));
}
-extern void deletePointerAssignmentsTUSummaryExtractor(
- PointerAssignmentsTUSummaryExtractor *Ptr) {
+extern void deletePointerFlowTUSummaryExtractor(
+ PointerFlowTUSummaryExtractor *Ptr) {
delete Ptr;
}
-extern EntityId addEntity(PointerAssignmentsTUSummaryExtractor &Extractor,
+extern EntityId addEntity(PointerFlowTUSummaryExtractor &Extractor,
EntityName &EN) {
return Extractor.addEntity(EN);
}
} // namespace clang::ssaf
-volatile int PointerAssignmentsTUSummaryExtractorAnchorSource = 0;
-static TUSummaryExtractorRegistry::Add<PointerAssignmentsTUSummaryExtractor>
- RegisterExtractor("PointerAssignmentsTUSummaryExtractor",
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int PointerFlowTUSummaryExtractorAnchorSource = 0;
+static TUSummaryExtractorRegistry::Add<PointerFlowTUSummaryExtractor>
+ RegisterExtractor("PointerFlowTUSummaryExtractor",
"The TUSummaryExtractor for pointer assignments");
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary.json
index d4c2aa93373c5..570ed5cfe07dd 100644
--- a/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary.json
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary.json
@@ -5,7 +5,7 @@
{
"entity_id": 2,
"entity_summary": {
- "PointerAssignments": [
+ "PointerFlow": [
[
[
{
@@ -38,7 +38,7 @@
}
}
],
- "summary_name": "PointerAssignments"
+ "summary_name": "PointerFlow"
},
{
"summary_data": [
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerAssignmentsTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
similarity index 87%
rename from clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerAssignmentsTest.cpp
rename to clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
index 790a344986f5e..53213a1ad13b8 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerAssignmentsTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
@@ -1,5 +1,4 @@
-//===- PointerAssignmentsTest.cpp
-//------------------------------------------===//
+//===- PointerFlowTest.cpp ------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -7,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerAssignments.h"
+#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
@@ -36,32 +35,32 @@ namespace clang::ssaf {
/////////////////////////////////////////////////////
// Declare Proxy functions
/////////////////////////////////////////////////////
-class PointerAssignmentsTUSummaryExtractor;
+class PointerFlowTUSummaryExtractor;
-extern Expected<std::unique_ptr<PointerAssignmentsEntitySummary>>
-extractEntitySummary(PointerAssignmentsTUSummaryExtractor &Extractor,
+extern Expected<std::unique_ptr<PointerFlowEntitySummary>>
+extractEntitySummary(PointerFlowTUSummaryExtractor &Extractor,
const NamedDecl *Contributor, ASTContext &Ctx);
-extern PointerAssignmentsTUSummaryExtractor *
-createPointerAssignmentsTUSummaryExtractor(TUSummaryBuilder &Builder);
+extern PointerFlowTUSummaryExtractor *
+createPointerFlowTUSummaryExtractor(TUSummaryBuilder &Builder);
-extern void deletePointerAssignmentsTUSummaryExtractor(
- PointerAssignmentsTUSummaryExtractor *);
+extern void deletePointerFlowTUSummaryExtractor(
+ PointerFlowTUSummaryExtractor *);
-extern EntityId addEntity(PointerAssignmentsTUSummaryExtractor &Extractor,
+extern EntityId addEntity(PointerFlowTUSummaryExtractor &Extractor,
EntityName &EN);
-class PointerAssignmentsTUSummaryExtractorProxy {
- PointerAssignmentsTUSummaryExtractor *Ptr;
+class PointerFlowTUSummaryExtractorProxy {
+ PointerFlowTUSummaryExtractor *Ptr;
public:
- explicit PointerAssignmentsTUSummaryExtractorProxy(TUSummaryBuilder &Builder)
- : Ptr(createPointerAssignmentsTUSummaryExtractor(Builder)) {}
- ~PointerAssignmentsTUSummaryExtractorProxy() {
- deletePointerAssignmentsTUSummaryExtractor(Ptr);
+ explicit PointerFlowTUSummaryExtractorProxy(TUSummaryBuilder &Builder)
+ : Ptr(createPointerFlowTUSummaryExtractor(Builder)) {}
+ ~PointerFlowTUSummaryExtractorProxy() {
+ deletePointerFlowTUSummaryExtractor(Ptr);
}
- PointerAssignmentsTUSummaryExtractor &operator*() const { return *Ptr; }
+ PointerFlowTUSummaryExtractor &operator*() const { return *Ptr; }
};
} // namespace clang::ssaf
@@ -178,21 +177,21 @@ struct EPLPair {
bool isFunRet;
};
-class PointerAssignmentsTest : public testing::Test {
+class PointerFlowTest : public testing::Test {
protected:
TUSummary TUSum;
TUSummaryBuilder Builder;
- PointerAssignmentsTUSummaryExtractorProxy ExtractorProxy;
+ PointerFlowTUSummaryExtractorProxy ExtractorProxy;
std::unique_ptr<ASTUnit> AST;
- PointerAssignmentsTest()
+ PointerFlowTest()
: TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
Builder(TUSum), ExtractorProxy(Builder) {}
template <typename ContributorDecl = NamedDecl,
typename =
std::enable_if_t<std::is_base_of_v<NamedDecl, ContributorDecl>>>
- std::unique_ptr<PointerAssignmentsEntitySummary>
+ std::unique_ptr<PointerFlowEntitySummary>
setUpTest(StringRef Code, FindEntityByName ContributorName) {
AST = tooling::buildASTFromCodeWithArgs(
Code, {"-Wno-unused-value", "-Wno-int-to-pointer-cast"});
@@ -241,7 +240,7 @@ class PointerAssignmentsTest : public testing::Test {
// 'ToEPL(Test, Line)' is a lambda that converts a 'EPLPair' to a
// 'EntityPointerLevel':
static constexpr auto ToEPL =
- [](PointerAssignmentsTest *Test,
+ [](PointerFlowTest *Test,
unsigned Line) -> std::function<EntityPointerLevel(const EPLPair &)> {
return [Test, Line](const EPLPair &Pair) -> EntityPointerLevel {
std::optional<EntityId> Entity = Pair.isFunRet
@@ -256,7 +255,7 @@ static constexpr auto ToEPL =
};
EdgeSet
-PointerAssignmentsTest::makeEdges(unsigned Line,
+PointerFlowTest::makeEdges(unsigned Line,
ArrayRef<std::pair<EPLPair, EPLPair>> Edges) {
EdgeSet Result;
for (auto Edge : Edges)
@@ -265,12 +264,12 @@ PointerAssignmentsTest::makeEdges(unsigned Line,
return Result;
}
-TEST_F(PointerAssignmentsTest, IsExtractorRegisteredTest) {
+TEST_F(PointerFlowTest, IsExtractorRegisteredTest) {
EXPECT_TRUE(
- isTUSummaryExtractorRegistered("PointerAssignmentsTUSummaryExtractor"));
+ isTUSummaryExtractorRegistered("PointerFlowTUSummaryExtractor"));
}
-TEST_F(PointerAssignmentsTest, IsJSONFormatRegistered) {
+TEST_F(PointerFlowTest, IsJSONFormatRegistered) {
std::set<llvm::StringRef> ActualNames;
for (const auto &Entry :
llvm::Registry<clang::ssaf::JSONFormat::FormatInfo>::entries()) {
@@ -278,7 +277,7 @@ TEST_F(PointerAssignmentsTest, IsJSONFormatRegistered) {
EXPECT_TRUE(Inserted);
}
- EXPECT_TRUE(ActualNames.count("PointerAssignments") == 1);
+ EXPECT_TRUE(ActualNames.count("PointerFlow") == 1);
}
//////////////////////////////////////////////////////////////
@@ -292,7 +291,7 @@ TEST_F(PointerAssignmentsTest, IsJSONFormatRegistered) {
// r = q;
// }
constexpr const char *const SerilizationTestOracle = R"cpp({
- "PointerAssignments": [
+ "PointerFlow": [
[
[
{
@@ -324,7 +323,7 @@ constexpr const char *const SerilizationTestOracle = R"cpp({
]
})cpp";
-TEST_F(PointerAssignmentsTest, SerializeTest) {
+TEST_F(PointerFlowTest, SerializeTest) {
auto Sum = setUpTest(R"cpp(
void foo(int ***p, int ****q, int x, int ****r) {
p[5][5][5];
@@ -346,7 +345,7 @@ TEST_F(PointerAssignmentsTest, SerializeTest) {
{*getEntityId("q"), 108},
{*getEntityId("r"), 9}};
- Object JData = PointerAssignmentsEntitySummary::summaryToJSON(
+ Object JData = PointerFlowEntitySummary::summaryToJSON(
*Sum, [&DummyTable](EntityId Id) {
return Object{{"@", Value(DummyTable[Id])}};
});
@@ -355,7 +354,7 @@ TEST_F(PointerAssignmentsTest, SerializeTest) {
SerilizationTestOracle);
}
-TEST_F(PointerAssignmentsTest, DeserializeTest) {
+TEST_F(PointerFlowTest, DeserializeTest) {
auto Sum = setUpTest(R"cpp(
void foo(int ***p, int ****q, int x, int ****r) {
p[5][5][5];
@@ -382,21 +381,21 @@ TEST_F(PointerAssignmentsTest, DeserializeTest) {
ASSERT_NE(ParsedJSON->getAsObject(), nullptr);
EntityIdTable Ignored;
- auto ParsedSum = PointerAssignmentsEntitySummary::summaryFromJSON(
+ auto ParsedSum = PointerFlowEntitySummary::summaryFromJSON(
*ParsedJSON->getAsObject(), Ignored,
[&DummyTable](const Object &O) -> Expected<EntityId> {
return DummyTable.at(O.getInteger("@").value());
});
ASSERT_THAT_EXPECTED(ParsedSum, llvm::Succeeded());
- EXPECT_EQ(*static_cast<PointerAssignmentsEntitySummary *>(ParsedSum->get()),
+ EXPECT_EQ(*static_cast<PointerFlowEntitySummary *>(ParsedSum->get()),
*Sum);
}
//////////////////////////////////////////////////////////////
// Simple Assign Tests //
//////////////////////////////////////////////////////////////
-TEST_F(PointerAssignmentsTest, SimpleAssign) {
+TEST_F(PointerFlowTest, SimpleAssign) {
auto Sum = setUpTest(R"cpp(
void foo(int *p, int *q) {
q = p;
@@ -408,7 +407,7 @@ TEST_F(PointerAssignmentsTest, SimpleAssign) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, AssignWithSubscriptLHS) {
+TEST_F(PointerFlowTest, AssignWithSubscriptLHS) {
auto Sum = setUpTest(R"cpp(
void foo(int **q, int *p, int x) {
q[x] = p;
@@ -420,7 +419,7 @@ TEST_F(PointerAssignmentsTest, AssignWithSubscriptLHS) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 2U}, {"p", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, AssignWithPtrArithRHS) {
+TEST_F(PointerFlowTest, AssignWithPtrArithRHS) {
auto Sum = setUpTest(R"cpp(
void foo(int *p, int *q) {
q = p + 5;
@@ -432,7 +431,7 @@ TEST_F(PointerAssignmentsTest, AssignWithPtrArithRHS) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, AssignInSubscript) {
+TEST_F(PointerFlowTest, AssignInSubscript) {
auto Sum = setUpTest(R"cpp(
void foo(int *p, int *q) {
(q = p)[5];
@@ -444,7 +443,7 @@ TEST_F(PointerAssignmentsTest, AssignInSubscript) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, MultipleAssign) {
+TEST_F(PointerFlowTest, MultipleAssign) {
auto Sum = setUpTest(R"cpp(
void foo(int *p, int *q, int *r) {
q = p;
@@ -460,7 +459,7 @@ TEST_F(PointerAssignmentsTest, MultipleAssign) {
}));
}
-TEST_F(PointerAssignmentsTest, ChainedAssign) {
+TEST_F(PointerFlowTest, ChainedAssign) {
auto Sum = setUpTest(R"cpp(
void foo(int *p, int *q, int *r) {
r = q = p;
@@ -475,7 +474,7 @@ TEST_F(PointerAssignmentsTest, ChainedAssign) {
}));
}
-TEST_F(PointerAssignmentsTest, CastToRValue) {
+TEST_F(PointerFlowTest, CastToRValue) {
auto Sum = setUpTest(R"cpp(
void foo(int *p, int *q) {
q = static_cast<int *&&>(p);
@@ -487,7 +486,7 @@ TEST_F(PointerAssignmentsTest, CastToRValue) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, AssignToMember) {
+TEST_F(PointerFlowTest, AssignToMember) {
auto Sum = setUpTest(R"cpp(
struct S { int *field; };
void foo(S s, int *p) {
@@ -500,7 +499,7 @@ TEST_F(PointerAssignmentsTest, AssignToMember) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"field", 1U}, {"p", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, AssignToMember2) {
+TEST_F(PointerFlowTest, AssignToMember2) {
auto Sum = setUpTest(R"cpp(
struct S { int *field; };
void foo(S *s, int *p) {
@@ -516,7 +515,7 @@ TEST_F(PointerAssignmentsTest, AssignToMember2) {
//////////////////////////////////////////////////////////////
// Call Expr Tests. //
//////////////////////////////////////////////////////////////
-TEST_F(PointerAssignmentsTest, CallArg) {
+TEST_F(PointerFlowTest, CallArg) {
auto Sum = setUpTest(R"cpp(
void bar(int *param);
void foo(int *p) {
@@ -529,7 +528,7 @@ TEST_F(PointerAssignmentsTest, CallArg) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"param", 1U}, {"p", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, CallMultiArgs) {
+TEST_F(PointerFlowTest, CallMultiArgs) {
auto Sum = setUpTest(R"cpp(
void bar(int *param1, int y, int *param2);
void foo(int *p, int x, int *q) {
@@ -545,7 +544,7 @@ TEST_F(PointerAssignmentsTest, CallMultiArgs) {
}));
}
-TEST_F(PointerAssignmentsTest, CallAsCallArg) {
+TEST_F(PointerFlowTest, CallAsCallArg) {
auto Sum = setUpTest(R"cpp(
int *bar(int * w);
@@ -560,7 +559,7 @@ TEST_F(PointerAssignmentsTest, CallAsCallArg) {
{{"p", 1U}, {"bar", 1U, true}}}));
}
-TEST_F(PointerAssignmentsTest, CXXOperatorCallMultiArgs) {
+TEST_F(PointerFlowTest, CXXOperatorCallMultiArgs) {
auto Sum = setUpTest(R"cpp(
struct S {
int* operator()(int *a, int *b);
@@ -580,7 +579,7 @@ TEST_F(PointerAssignmentsTest, CXXOperatorCallMultiArgs) {
}));
}
-TEST_F(PointerAssignmentsTest, CXXMemberCall) {
+TEST_F(PointerFlowTest, CXXMemberCall) {
auto Sum = setUpTest(R"cpp(
struct S {
int* method(int *a, int *b);
@@ -598,7 +597,7 @@ TEST_F(PointerAssignmentsTest, CXXMemberCall) {
{{"q", 1U}, {"method", 1U, true}}}));
}
-TEST_F(PointerAssignmentsTest, VirtualMethodCall) {
+TEST_F(PointerFlowTest, VirtualMethodCall) {
auto Sum = setUpTest(R"cpp(
struct Base {
virtual void method(int *a);
@@ -613,7 +612,7 @@ TEST_F(PointerAssignmentsTest, VirtualMethodCall) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"a", 1U}, {"p", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, StaticMethodCall) {
+TEST_F(PointerFlowTest, StaticMethodCall) {
auto Sum = setUpTest(R"cpp(
struct S {
static void method(int *a, int *b);
@@ -631,7 +630,7 @@ TEST_F(PointerAssignmentsTest, StaticMethodCall) {
}));
}
-TEST_F(PointerAssignmentsTest, DefaultArg) {
+TEST_F(PointerFlowTest, DefaultArg) {
auto Sum = setUpTest(R"cpp(
int *g;
void bar(int *a, int *b = g);
@@ -646,7 +645,7 @@ TEST_F(PointerAssignmentsTest, DefaultArg) {
{{{"a", 1U}, {"p", 1U}}, {{"b", 1U}, {"g", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, DefaultArg2) {
+TEST_F(PointerFlowTest, DefaultArg2) {
auto Sum = setUpTest(R"cpp(
int *g;
void bar(int *a, int *b = g);
@@ -666,7 +665,7 @@ TEST_F(PointerAssignmentsTest, DefaultArg2) {
//////////////////////////////////////////////////////////////
// CXX Ctor Tests. //
//////////////////////////////////////////////////////////////
-TEST_F(PointerAssignmentsTest, CXXCtorCallMultiArgs) {
+TEST_F(PointerFlowTest, CXXCtorCallMultiArgs) {
auto Sum = setUpTest(R"cpp(
struct S {
S(int *a, int *b) {}
@@ -684,7 +683,7 @@ TEST_F(PointerAssignmentsTest, CXXCtorCallMultiArgs) {
}));
}
-TEST_F(PointerAssignmentsTest, CXXCtorCallMultiArgs2) {
+TEST_F(PointerFlowTest, CXXCtorCallMultiArgs2) {
auto Sum = setUpTest(R"cpp(
struct S {
S(int *a, int x, int *b) {}
@@ -702,7 +701,7 @@ TEST_F(PointerAssignmentsTest, CXXCtorCallMultiArgs2) {
}));
}
-TEST_F(PointerAssignmentsTest, CXXCtorCallAsCallArg) {
+TEST_F(PointerFlowTest, CXXCtorCallAsCallArg) {
auto Sum = setUpTest(R"cpp(
struct Wrapper {
Wrapper(int *q) {}
@@ -718,7 +717,7 @@ TEST_F(PointerAssignmentsTest, CXXCtorCallAsCallArg) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, DelegatingCXXCtorCall) {
+TEST_F(PointerFlowTest, DelegatingCXXCtorCall) {
auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
struct S {
S(int *a, int *b) {}
@@ -734,7 +733,7 @@ TEST_F(PointerAssignmentsTest, DelegatingCXXCtorCall) {
}));
}
-TEST_F(PointerAssignmentsTest, CXXCtorBaseInit) {
+TEST_F(PointerFlowTest, CXXCtorBaseInit) {
auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
struct Base {
Base(int *a) {}
@@ -752,7 +751,7 @@ TEST_F(PointerAssignmentsTest, CXXCtorBaseInit) {
//////////////////////////////////////////////////////////////
// Initializers Tests. //
//////////////////////////////////////////////////////////////
-TEST_F(PointerAssignmentsTest, LocalVarDeclInit) {
+TEST_F(PointerFlowTest, LocalVarDeclInit) {
auto Sum = setUpTest(R"cpp(
void foo(int *p) {
int *q = p;
@@ -764,7 +763,7 @@ TEST_F(PointerAssignmentsTest, LocalVarDeclInit) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, LocalVarDeclInit2) {
+TEST_F(PointerFlowTest, LocalVarDeclInit2) {
auto Sum = setUpTest(R"cpp(
void foo(int (*arr)[10]) {
int (*p)[10] = arr;
@@ -776,7 +775,7 @@ TEST_F(PointerAssignmentsTest, LocalVarDeclInit2) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"p", 1U}, {"arr", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, FieldInit) {
+TEST_F(PointerFlowTest, FieldInit) {
auto Sum = setUpTest(R"cpp(
void foo(int *p) {
struct Bar {
@@ -790,7 +789,7 @@ TEST_F(PointerAssignmentsTest, FieldInit) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"field", 1U}, {"p", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, CXXCtorMemberInit) {
+TEST_F(PointerFlowTest, CXXCtorMemberInit) {
StringRef Code = R"cpp(
void foo(int *p) {
struct Bar {
@@ -812,7 +811,7 @@ TEST_F(PointerAssignmentsTest, CXXCtorMemberInit) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, GlobalVarInit) {
+TEST_F(PointerFlowTest, GlobalVarInit) {
auto Sum = setUpTest<VarDecl>(R"cpp(
int *q;
int *g = q;
@@ -823,7 +822,7 @@ TEST_F(PointerAssignmentsTest, GlobalVarInit) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"g", 1U}, {"q", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, StaticLocalInit) {
+TEST_F(PointerFlowTest, StaticLocalInit) {
auto Sum = setUpTest(R"cpp(
void foo(int *p) {
static int *s = p;
@@ -835,7 +834,7 @@ TEST_F(PointerAssignmentsTest, StaticLocalInit) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"s", 1U}, {"p", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, StaticMemberInit) {
+TEST_F(PointerFlowTest, StaticMemberInit) {
auto Sum = setUpTest<VarDecl>(R"cpp(
int *g;
struct S { static int *member = g; };
@@ -850,7 +849,7 @@ TEST_F(PointerAssignmentsTest, StaticMemberInit) {
// InitList Tests. //
//////////////////////////////////////////////////////////////
-TEST_F(PointerAssignmentsTest, ArrayInitList) {
+TEST_F(PointerFlowTest, ArrayInitList) {
auto Sum = setUpTest(R"cpp(
void foo(int *p, int *q) {
int *arr[] = {p, q};
@@ -865,7 +864,7 @@ TEST_F(PointerAssignmentsTest, ArrayInitList) {
}));
}
-TEST_F(PointerAssignmentsTest, StructInitList) {
+TEST_F(PointerFlowTest, StructInitList) {
auto Sum = setUpTest(R"cpp(
struct S { int *a; int *b; };
void foo(int *p, int *q) {
@@ -882,7 +881,7 @@ TEST_F(PointerAssignmentsTest, StructInitList) {
}
// A union initialized with a brace-enclosed initializer:
-TEST_F(PointerAssignmentsTest, UnionInitList) {
+TEST_F(PointerFlowTest, UnionInitList) {
auto Sum = setUpTest(R"cpp(
union U { int *x; int y; };
void foo(int *p) {
@@ -895,7 +894,7 @@ TEST_F(PointerAssignmentsTest, UnionInitList) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"x", 1U}, {"p", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, NestedInitList) {
+TEST_F(PointerFlowTest, NestedInitList) {
auto Sum = setUpTest(R"cpp(
struct Inner { int * a; int * b; };
struct S { Inner c; int * d; };
@@ -913,7 +912,7 @@ TEST_F(PointerAssignmentsTest, NestedInitList) {
}));
}
-TEST_F(PointerAssignmentsTest, NestedInitList2) {
+TEST_F(PointerFlowTest, NestedInitList2) {
auto Sum = setUpTest(R"cpp(
union Inner { int * a; int b; };
struct S { Inner c; int * d; };
@@ -930,7 +929,7 @@ TEST_F(PointerAssignmentsTest, NestedInitList2) {
}));
}
-TEST_F(PointerAssignmentsTest, NestedInitList3) {
+TEST_F(PointerFlowTest, NestedInitList3) {
auto Sum = setUpTest(R"cpp(
struct Inner { int * a; int * b; };
union S { Inner c; int * d; };
@@ -947,7 +946,7 @@ TEST_F(PointerAssignmentsTest, NestedInitList3) {
}));
}
-TEST_F(PointerAssignmentsTest, NestedArrayInitList) {
+TEST_F(PointerFlowTest, NestedArrayInitList) {
auto Sum = setUpTest(R"cpp(
void foo(int *p, int *q, int *r, int *s) {
int *arr[][2] = {{p, q}, {r, s}};
@@ -964,7 +963,7 @@ TEST_F(PointerAssignmentsTest, NestedArrayInitList) {
}));
}
-TEST_F(PointerAssignmentsTest, MixedNestedArrayStructInitList) {
+TEST_F(PointerFlowTest, MixedNestedArrayStructInitList) {
auto Sum = setUpTest(R"cpp(
struct T { int *arr[2]; };
void foo(int *p, int *q, int *r, int *s) {
@@ -982,7 +981,7 @@ TEST_F(PointerAssignmentsTest, MixedNestedArrayStructInitList) {
}));
}
-TEST_F(PointerAssignmentsTest, ArrayOfStructInitList) {
+TEST_F(PointerFlowTest, ArrayOfStructInitList) {
auto Sum = setUpTest(R"cpp(
struct S { int *a; int *b; };
void foo(int *p, int *q, int *r, int *s) {
@@ -1004,7 +1003,7 @@ TEST_F(PointerAssignmentsTest, ArrayOfStructInitList) {
// Return Tests. //
//////////////////////////////////////////////////////////////
-TEST_F(PointerAssignmentsTest, ReturnEdge) {
+TEST_F(PointerFlowTest, ReturnEdge) {
auto Sum = setUpTest(R"cpp(
int *foo(int *p) {
return p;
@@ -1016,7 +1015,7 @@ TEST_F(PointerAssignmentsTest, ReturnEdge) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"foo", 1U, true}, {"p", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, MultipleReturnEdges) {
+TEST_F(PointerFlowTest, MultipleReturnEdges) {
auto Sum = setUpTest(R"cpp(
int *foo(int *p, int *q, bool cond) {
if (cond)
@@ -1033,7 +1032,7 @@ TEST_F(PointerAssignmentsTest, MultipleReturnEdges) {
}));
}
-TEST_F(PointerAssignmentsTest, NoReturnEdgeForNonPointerReturnType) {
+TEST_F(PointerFlowTest, NoReturnEdgeForNonPointerReturnType) {
auto Sum = setUpTest(R"cpp(
int foo(int *p, int x) {
return x;
@@ -1045,7 +1044,7 @@ TEST_F(PointerAssignmentsTest, NoReturnEdgeForNonPointerReturnType) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {}));
}
-TEST_F(PointerAssignmentsTest, ReturnEdgeNotFromNestedFunction) {
+TEST_F(PointerFlowTest, ReturnEdgeNotFromNestedFunction) {
auto *Code = R"cpp(
int *foo(int *p) {
struct Inner {
@@ -1065,7 +1064,7 @@ TEST_F(PointerAssignmentsTest, ReturnEdgeNotFromNestedFunction) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"bar", 1U, true}, {"q", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, ReturnEdgeInClassMethod) {
+TEST_F(PointerFlowTest, ReturnEdgeInClassMethod) {
auto Sum = setUpTest(R"cpp(
void foo() {
struct S {
@@ -1079,7 +1078,7 @@ TEST_F(PointerAssignmentsTest, ReturnEdgeInClassMethod) {
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"method", 1U, true}, {"p", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, NoEdgeFromIndirectCall) {
+TEST_F(PointerFlowTest, NoEdgeFromIndirectCall) {
auto Sum = setUpTest(R"cpp(
void bar(int *param1);
void baz(int *param2);
@@ -1108,7 +1107,7 @@ TEST_F(PointerAssignmentsTest, NoEdgeFromIndirectCall) {
// Lambda Tests. //
//////////////////////////////////////////////////////////////
-TEST_F(PointerAssignmentsTest, ReturnInLambda) {
+TEST_F(PointerFlowTest, ReturnInLambda) {
StringRef Code = R"cpp(
int* foo(int *p) {
auto local = [](int *r) { return r; };
@@ -1129,7 +1128,7 @@ TEST_F(PointerAssignmentsTest, ReturnInLambda) {
{{{LambdaOfVar{"local"}, 1U, true}, {"r", 1U}}}));
}
-TEST_F(PointerAssignmentsTest, NestedLambdaAssign) {
+TEST_F(PointerFlowTest, NestedLambdaAssign) {
StringRef Code = R"cpp(
void foo() {
auto outer_lambda = [](int *r, int *s) {
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
index 245ee4cb856ab..d853dd1b79df5 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -1,5 +1,5 @@
add_distinct_clang_unittest(ClangScalableAnalysisTests
- Analyses/PointerAssignmentsTest.cpp
+ Analyses/PointerFlow/PointerFlowTest.cpp
Analyses/CallGraph/CallGraphExtractorTest.cpp
Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp
ASTEntityMappingTest.cpp
>From 5730becfa09978e7738a6197182dc84a9585cac4 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Tue, 7 Apr 2026 18:05:33 -0700
Subject: [PATCH 42/51] improve the PR
---
.../Analyses/PointerFlow/PointerFlow.h | 27 +---
.../Analyses/PointerFlow/PointerFlow.cpp | 44 +++---
.../Inputs/tu-summary-bad-array.json | 88 ++++++++++++
.../Inputs/tu-summary-bad-summary.json | 65 +++++++++
.../PointerFlow/Inputs/tu-summary-no-key.json | 94 +++++++++++++
.../PointerFlow/Inputs/tu-summary.json | 94 +++++++++++++
.../PointerFlow/tu-summary-serialization.test | 27 ++++
.../Analysis/Scalable/ssaf-format/list.test | 1 +
.../Analyses/PointerFlow/PointerFlowTest.cpp | 125 ------------------
9 files changed, 404 insertions(+), 161 deletions(-)
create mode 100644 clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-bad-array.json
create mode 100644 clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-bad-summary.json
create mode 100644 clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-no-key.json
create mode 100644 clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary.json
create mode 100644 clang/test/Analysis/Scalable/PointerFlow/tu-summary-serialization.test
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
index b95c42328ee69..2bb0974de92a4 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
@@ -11,11 +11,10 @@
// nodes.
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERASSIGNMENTS_H
-#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERASSIGNMENTS_H
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERFLOW_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERFLOW_H
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
namespace clang::ssaf {
@@ -27,6 +26,9 @@ class PointerFlowEntitySummary final : public EntitySummary {
EdgeSet Edges;
friend class PointerFlowTUSummaryExtractor;
+ friend PointerFlowEntitySummary buildPointerFlowEntitySummary(EdgeSet Edges);
+ friend llvm::iterator_range<EdgeSet::const_iterator>
+ getEdges(const PointerFlowEntitySummary &);
PointerFlowEntitySummary(EdgeSet Edges)
: EntitySummary(), Edges(std::move(Edges)) {}
@@ -42,25 +44,8 @@ class PointerFlowEntitySummary final : public EntitySummary {
bool empty() const { return Edges.empty() && Edges.empty(); }
- static llvm::json::Object
- summaryToJSON(const EntitySummary &ES,
- JSONFormat::EntityIdToJSONFn EntityId2JSON);
-
- static llvm::Expected<std::unique_ptr<EntitySummary>>
- summaryFromJSON(const llvm::json::Object &Data, EntityIdTable &,
- JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
-
static SummaryName summaryName() { return SummaryName{"PointerFlow"}; }
};
-
-struct PointerFlowJSONFormatInfo : JSONFormat::FormatInfo {
- PointerFlowJSONFormatInfo()
- : JSONFormat::FormatInfo(
- PointerFlowEntitySummary::summaryName(),
- PointerFlowEntitySummary::summaryToJSON,
- PointerFlowEntitySummary::summaryFromJSON) {}
-};
-
} // namespace clang::ssaf
-#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERASSIGNMENTS_H
\ No newline at end of file
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_POINTERFLOW_POINTERFLOW_H
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
index 58aea3461c9c2..1ca731e4f58ac 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
@@ -10,19 +10,29 @@
#include "SSAFAnalysesCommon.h"
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
-#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/JSON.h"
+using namespace clang;
+using namespace ssaf;
+using Object = llvm::json::Object;
+using Array = llvm::json::Array;
+using Value = llvm::json::Value;
+
namespace {
constexpr const char *const PointerFlowKey = "PointerFlow";
} // namespace
-namespace clang::ssaf {
-using Object = llvm::json::Object;
-using Array = llvm::json::Array;
-using Value = llvm::json::Value;
+ssaf::PointerFlowEntitySummary
+ssaf::buildPointerFlowEntitySummary(EdgeSet Edges) {
+ return PointerFlowEntitySummary(std::move(Edges));
+}
+
+llvm::iterator_range<EdgeSet::const_iterator>
+ssaf::getEdges(const PointerFlowEntitySummary &Sum) {
+ return Sum.Edges;
+}
// Writes the 'Edges' map as an array of array of EntityPointerLevels:
// Array [
@@ -30,12 +40,13 @@ using Value = llvm::json::Value;
// Array [ [lhs-node], [rhs-node], [rhs-node], ...]
// ...
// ]
-llvm::json::Object PointerFlowEntitySummary::summaryToJSON(
- const EntitySummary &ES, JSONFormat::EntityIdToJSONFn EntityId2JSON) {
+static llvm::json::Object
+summaryToJSON(const EntitySummary &ES,
+ JSONFormat::EntityIdToJSONFn EntityId2JSON) {
Array EdgesData;
for (const auto &Entry :
- static_cast<const PointerFlowEntitySummary &>(ES).Edges) {
+ getEdges(static_cast<const PointerFlowEntitySummary &>(ES))) {
Array EdgesEntryData;
EntityPointerLevel LHS = Entry.first;
@@ -52,10 +63,9 @@ llvm::json::Object PointerFlowEntitySummary::summaryToJSON(
return Data;
}
-llvm::Expected<std::unique_ptr<EntitySummary>>
-PointerFlowEntitySummary::summaryFromJSON(
- const Object &Data, EntityIdTable &,
- JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
+static llvm::Expected<std::unique_ptr<EntitySummary>>
+summaryFromJSON(const Object &Data, EntityIdTable &,
+ JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
const Value *EdgesData = Data.get(PointerFlowKey);
if (!EdgesData)
@@ -91,14 +101,18 @@ PointerFlowEntitySummary::summaryFromJSON(
Edges[*EPLs.begin()].insert(EPLs.begin() + 1, EPLs.end());
}
return std::make_unique<PointerFlowEntitySummary>(
- PointerFlowEntitySummary(std::move(Edges)));
+ buildPointerFlowEntitySummary(std::move(Edges)));
}
+struct PointerFlowJSONFormatInfo : JSONFormat::FormatInfo {
+ PointerFlowJSONFormatInfo()
+ : JSONFormat::FormatInfo(PointerFlowEntitySummary::summaryName(),
+ summaryToJSON, summaryFromJSON) {}
+};
+
static llvm::Registry<JSONFormat::FormatInfo>::Add<PointerFlowJSONFormatInfo>
RegisterPointerFlowJSONFormatInfo(
"PointerFlow", "JSON Format info for PointerFlowEntitySummary");
-} // namespace clang::ssaf
-
// NOLINTNEXTLINE(misc-use-internal-linkage)
volatile int PointerFlowSSAFJSONFormatAnchorSource = 0;
\ No newline at end of file
diff --git a/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-bad-array.json b/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-bad-array.json
new file mode 100644
index 0000000000000..3848bd68ac0fb
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-bad-array.json
@@ -0,0 +1,88 @@
+{
+ "data": [
+ {
+ "summary_data": [
+ {
+ "entity_id": 2,
+ "entity_summary": {
+ "PointerFlow": [
+ [
+ [
+ {
+ "@": 0
+ },
+ 2
+ ]
+ ],
+ [
+ [
+ {
+ "@": 2
+ },
+ 1
+ ],
+ [
+ {
+ "@": 0
+ },
+ 1
+ ]
+ ]
+ ]
+ }
+ }
+ ],
+ "summary_name": "PointerFlow"
+ }
+ ],
+ "id_table": [
+ {
+ "id": 2,
+ "name": {
+ "namespace": [],
+ "suffix": "",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ },
+ {
+ "id": 0,
+ "name": {
+ "namespace": [],
+ "suffix": "1",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ },
+ {
+ "id": 1,
+ "name": {
+ "namespace": [],
+ "suffix": "2",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ },
+ {
+ "id": 1,
+ "linkage": {
+ "type": "Internal"
+ }
+ },
+ {
+ "id": 2,
+ "linkage": {
+ "type": "Internal"
+ }
+ }
+ ],
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "Mock.cpp"
+ }
+}
diff --git a/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-bad-summary.json b/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-bad-summary.json
new file mode 100644
index 0000000000000..77c02313b6add
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-bad-summary.json
@@ -0,0 +1,65 @@
+{
+ "data": [
+ {
+ "summary_data": [
+ {
+ "entity_id": 2,
+ "entity_summary": {
+ "PointerFlow": {}
+ }
+ }
+ ],
+ "summary_name": "PointerFlow"
+ }
+ ],
+ "id_table": [
+ {
+ "id": 2,
+ "name": {
+ "namespace": [],
+ "suffix": "",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ },
+ {
+ "id": 0,
+ "name": {
+ "namespace": [],
+ "suffix": "1",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ },
+ {
+ "id": 1,
+ "name": {
+ "namespace": [],
+ "suffix": "2",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ },
+ {
+ "id": 1,
+ "linkage": {
+ "type": "Internal"
+ }
+ },
+ {
+ "id": 2,
+ "linkage": {
+ "type": "Internal"
+ }
+ }
+ ],
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "Mock.cpp"
+ }
+}
diff --git a/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-no-key.json b/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-no-key.json
new file mode 100644
index 0000000000000..e950edbfa1f3c
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary-no-key.json
@@ -0,0 +1,94 @@
+{
+ "data": [
+ {
+ "summary_data": [
+ {
+ "entity_id": 2,
+ "entity_summary": {
+ "NoPointerFlow": [
+ [
+ [
+ {
+ "@": 0
+ },
+ 2
+ ],
+ [
+ {
+ "@": 1
+ },
+ 1
+ ]
+ ],
+ [
+ [
+ {
+ "@": 2
+ },
+ 1
+ ],
+ [
+ {
+ "@": 0
+ },
+ 1
+ ]
+ ]
+ ]
+ }
+ }
+ ],
+ "summary_name": "PointerFlow"
+ }
+ ],
+ "id_table": [
+ {
+ "id": 2,
+ "name": {
+ "namespace": [],
+ "suffix": "",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ },
+ {
+ "id": 0,
+ "name": {
+ "namespace": [],
+ "suffix": "1",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ },
+ {
+ "id": 1,
+ "name": {
+ "namespace": [],
+ "suffix": "2",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ },
+ {
+ "id": 1,
+ "linkage": {
+ "type": "Internal"
+ }
+ },
+ {
+ "id": 2,
+ "linkage": {
+ "type": "Internal"
+ }
+ }
+ ],
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "Mock.cpp"
+ }
+}
diff --git a/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary.json b/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary.json
new file mode 100644
index 0000000000000..b90cdbb574b15
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/Inputs/tu-summary.json
@@ -0,0 +1,94 @@
+{
+ "data": [
+ {
+ "summary_data": [
+ {
+ "entity_id": 2,
+ "entity_summary": {
+ "PointerFlow": [
+ [
+ [
+ {
+ "@": 0
+ },
+ 2
+ ],
+ [
+ {
+ "@": 1
+ },
+ 1
+ ]
+ ],
+ [
+ [
+ {
+ "@": 2
+ },
+ 1
+ ],
+ [
+ {
+ "@": 0
+ },
+ 1
+ ]
+ ]
+ ]
+ }
+ }
+ ],
+ "summary_name": "PointerFlow"
+ }
+ ],
+ "id_table": [
+ {
+ "id": 2,
+ "name": {
+ "namespace": [],
+ "suffix": "",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ },
+ {
+ "id": 0,
+ "name": {
+ "namespace": [],
+ "suffix": "1",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ },
+ {
+ "id": 1,
+ "name": {
+ "namespace": [],
+ "suffix": "2",
+ "usr": "c:@F at foo#***I#*S0_#I#"
+ }
+ }
+ ],
+ "linkage_table": [
+ {
+ "id": 0,
+ "linkage": {
+ "type": "External"
+ }
+ },
+ {
+ "id": 1,
+ "linkage": {
+ "type": "Internal"
+ }
+ },
+ {
+ "id": 2,
+ "linkage": {
+ "type": "Internal"
+ }
+ }
+ ],
+ "tu_namespace": {
+ "kind": "CompilationUnit",
+ "name": "Mock.cpp"
+ }
+}
diff --git a/clang/test/Analysis/Scalable/PointerFlow/tu-summary-serialization.test b/clang/test/Analysis/Scalable/PointerFlow/tu-summary-serialization.test
new file mode 100644
index 0000000000000..4754e61637f34
--- /dev/null
+++ b/clang/test/Analysis/Scalable/PointerFlow/tu-summary-serialization.test
@@ -0,0 +1,27 @@
+// RUN: rm -rf %t.json
+// RUN: clang-ssaf-format -type=tu %S/Inputs/tu-summary.json -o %t.json
+// RUN: diff %S/Inputs/tu-summary.json %t.json
+
+// 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: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-summary.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-BAD-SUMMARY
+// CHECK-BAD-SUMMARY: saw {} but expected a JSON array of arary of EntityPointerLevels
+
+// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-array.json 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-BAD-ARRAY
+// CHECK-BAD-ARRAY: saw [
+// CHECK-BAD-ARRAY: [
+// CHECK-BAD-ARRAY: {
+// CHECK-BAD-ARRAY: "@": 0
+// CHECK-BAD-ARRAY: },
+// CHECK-BAD-ARRAY: 2
+// CHECK-BAD-ARRAY: ]
+// CHECK-BAD-ARRAY:] but expected a JSON array of EntityPointerLevels with a size greater than 1: [lhs, rhs, rhs, ...]
diff --git a/clang/test/Analysis/Scalable/ssaf-format/list.test b/clang/test/Analysis/Scalable/ssaf-format/list.test
index c56bec1365f29..d5e8a229aedf2 100644
--- a/clang/test/Analysis/Scalable/ssaf-format/list.test
+++ b/clang/test/Analysis/Scalable/ssaf-format/list.test
@@ -9,3 +9,4 @@
// CHECK-DAG: Analyses:
// CHECK-DAG: [[NthFormat]].[[MthSummary:[0-9]+]]. CallGraph - JSON Format info for CallGraph summary
// CHECK-DAG: [[NthFormat]].[[MthSummary:[0-9]+]]. UnsafeBufferUsage - JSON Format info for UnsafeBufferUsageEntitySummary
+// CHECK-DAG: [[NthFormat]].[[MthSummary:[0-9]+]]. PointerFlow - JSON Format info for PointerFlowEntitySummary
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
index 53213a1ad13b8..2a3fb8ef9ee72 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
@@ -15,14 +15,12 @@
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
-#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
-#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
#include <memory>
#include <type_traits>
@@ -269,129 +267,6 @@ TEST_F(PointerFlowTest, IsExtractorRegisteredTest) {
isTUSummaryExtractorRegistered("PointerFlowTUSummaryExtractor"));
}
-TEST_F(PointerFlowTest, IsJSONFormatRegistered) {
- std::set<llvm::StringRef> ActualNames;
- for (const auto &Entry :
- llvm::Registry<clang::ssaf::JSONFormat::FormatInfo>::entries()) {
- bool Inserted = ActualNames.insert(Entry.getName()).second;
- EXPECT_TRUE(Inserted);
- }
-
- EXPECT_TRUE(ActualNames.count("PointerFlow") == 1);
-}
-
-//////////////////////////////////////////////////////////////
-// JSON Tests //
-//////////////////////////////////////////////////////////////
-// Oracle JSON output for the example:
-// void foo(int ***p, int ****q, int x, int ****r) {
-// p[5][5][5];
-// q[5][5][5][5];
-// q[x] = p;
-// r = q;
-// }
-constexpr const char *const SerilizationTestOracle = R"cpp({
- "PointerFlow": [
- [
- [
- {
- "@": 108
- },
- 2
- ],
- [
- {
- "@": 42
- },
- 1
- ]
- ],
- [
- [
- {
- "@": 9
- },
- 1
- ],
- [
- {
- "@": 108
- },
- 1
- ]
- ]
- ]
-})cpp";
-
-TEST_F(PointerFlowTest, SerializeTest) {
- auto Sum = setUpTest(R"cpp(
- void foo(int ***p, int ****q, int x, int ****r) {
- p[5][5][5];
- q[5][5][5][5];
- q[x] = p;
- r = q;
- }
- )cpp",
- "foo");
- ASSERT_NE(Sum, nullptr);
- EXPECT_EQ(*Sum, makeEdges(__LINE__, {
- /*[0]=*/{{"q", 2U}, {"p", 1U}},
- /*[1]=*/{{"r", 1U}, {"q", 1U}},
- }));
-
- using Object = llvm::json::Object;
- using Value = llvm::json::Value;
- std::map<EntityId, uint64_t> DummyTable{{*getEntityId("p"), 42},
- {*getEntityId("q"), 108},
- {*getEntityId("r"), 9}};
-
- Object JData = PointerFlowEntitySummary::summaryToJSON(
- *Sum, [&DummyTable](EntityId Id) {
- return Object{{"@", Value(DummyTable[Id])}};
- });
-
- EXPECT_EQ(llvm::formatv("{0:2}", llvm::json::Value(std::move(JData))).str(),
- SerilizationTestOracle);
-}
-
-TEST_F(PointerFlowTest, DeserializeTest) {
- auto Sum = setUpTest(R"cpp(
- void foo(int ***p, int ****q, int x, int ****r) {
- p[5][5][5];
- q[5][5][5][5];
- q[x] = p;
- r = q;
- }
- )cpp",
- "foo");
- ASSERT_NE(Sum, nullptr);
- EXPECT_EQ(*Sum, makeEdges(__LINE__, {
- /*[0]=*/{{"q", 2U}, {"p", 1U}},
- /*[1]=*/{{"r", 1U}, {"q", 1U}},
- }));
-
- using Object = llvm::json::Object;
- using Value = llvm::json::Value;
- std::map<uint64_t, EntityId> DummyTable{{42, *getEntityId("p")},
- {108, *getEntityId("q")},
- {9, *getEntityId("r")}};
- Expected<Value> ParsedJSON = llvm::json::parse(SerilizationTestOracle);
-
- ASSERT_THAT_EXPECTED(ParsedJSON, llvm::Succeeded());
- ASSERT_NE(ParsedJSON->getAsObject(), nullptr);
-
- EntityIdTable Ignored;
- auto ParsedSum = PointerFlowEntitySummary::summaryFromJSON(
- *ParsedJSON->getAsObject(), Ignored,
- [&DummyTable](const Object &O) -> Expected<EntityId> {
- return DummyTable.at(O.getInteger("@").value());
- });
-
- ASSERT_THAT_EXPECTED(ParsedSum, llvm::Succeeded());
- EXPECT_EQ(*static_cast<PointerFlowEntitySummary *>(ParsedSum->get()),
- *Sum);
-}
-
//////////////////////////////////////////////////////////////
// Simple Assign Tests //
//////////////////////////////////////////////////////////////
>From 492ec75ee199697b669deb2850689b9923bc2dd1 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 8 Apr 2026 11:58:36 -0700
Subject: [PATCH 43/51] refactor tests to use HandleTranslationUnit, get rid of
proxy functions
---
.../Analyses/PointerFlow/PointerFlow.h | 4 +-
.../PointerFlow/PointerFlowExtractor.cpp | 78 +--
.../Analyses/SSAFAnalysesCommon.h | 11 +-
.../Analyses/PointerFlow/PointerFlowTest.cpp | 497 ++++++++++--------
4 files changed, 324 insertions(+), 266 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
index 2bb0974de92a4..b93decd701f05 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
@@ -34,6 +34,8 @@ class PointerFlowEntitySummary final : public EntitySummary {
: EntitySummary(), Edges(std::move(Edges)) {}
public:
+ static constexpr llvm::StringLiteral Name = "PointerFlow";
+
SummaryName getSummaryName() const override { return summaryName(); };
bool operator==(const EdgeSet &Other) const { return Edges == Other; }
@@ -44,7 +46,7 @@ class PointerFlowEntitySummary final : public EntitySummary {
bool empty() const { return Edges.empty() && Edges.empty(); }
- static SummaryName summaryName() { return SummaryName{"PointerFlow"}; }
+ static SummaryName summaryName() { return SummaryName{Name.str()}; }
};
} // namespace clang::ssaf
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
index 49e682a199309..16eeb70dfe25c 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
@@ -315,73 +315,35 @@ class PointerFlowTUSummaryExtractor : public TUSummaryExtractor {
PointerFlowEntitySummary(std::move(Matcher.Results)));
}
- void HandleTranslationUnit(ASTContext &Ctx) override;
-};
+ void HandleTranslationUnit(ASTContext &Ctx) override {
+ ContributorFinder ContributorFinder;
-void PointerFlowTUSummaryExtractor::HandleTranslationUnit(
- ASTContext &Ctx) {
- llvm::Error Errors = llvm::ErrorSuccess();
- auto addError = [&Errors](llvm::Error Err) {
- Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
- };
- ContributorFinder ContributorFinder;
-
- ContributorFinder.VisitTranslationUnitDecl(Ctx.getTranslationUnitDecl());
- for (auto *CD : ContributorFinder.Contributors) {
- auto EntitySummary = extractEntitySummary(CD, Ctx);
-
- if (!EntitySummary) {
- addError(EntitySummary.takeError());
- continue;
- }
- assert(*EntitySummary &&
- "std::unique_ptr<EntitySummary> should not be null");
- if ((*EntitySummary)->empty())
- continue;
+ ContributorFinder.TraverseAST(Ctx);
+ for (auto *CD : ContributorFinder.Contributors) {
+ auto EntitySummary = extractEntitySummary(CD, Ctx);
- auto ContributorName = getEntityName(CD);
+ if (!EntitySummary)
+ llvm::reportFatalInternalError(EntitySummary.takeError());
+ assert(*EntitySummary);
+ if ((*EntitySummary)->empty())
+ continue;
- if (!ContributorName) {
- addError(makeEntityNameErr(Ctx, *CD));
- continue;
- }
+ auto ContributorName = getEntityName(CD);
- auto [EntitySummaryPtr, Success] = SummaryBuilder.addSummary(
- addEntity(*ContributorName), std::move(*EntitySummary));
+ if (!ContributorName)
+ llvm::reportFatalInternalError(makeEntityNameErr(Ctx, *CD));
- if (!Success)
- addError(makeAddEntitySummaryErr(Ctx, CD));
+ auto [Ignored, InsertionSucceeded] = SummaryBuilder.addSummary(
+ addEntity(*ContributorName), std::move(*EntitySummary));
+
+ assert(InsertionSucceeded && "duplicated contributor extraction");
+ }
}
- // FIXME: handle errors!
- llvm::consumeError(std::move(Errors));
-}
-
-// Proxy functions for unit test:
-extern Expected<std::unique_ptr<PointerFlowEntitySummary>>
-extractEntitySummary(PointerFlowTUSummaryExtractor &Extractor,
- const NamedDecl *Contributor, ASTContext &Ctx) {
- return Extractor.extractEntitySummary(Contributor, Ctx);
-}
-
-extern PointerFlowTUSummaryExtractor *
-createPointerFlowTUSummaryExtractor(TUSummaryBuilder &Builder) {
- return new PointerFlowTUSummaryExtractor(
- PointerFlowTUSummaryExtractor(Builder));
-}
-
-extern void deletePointerFlowTUSummaryExtractor(
- PointerFlowTUSummaryExtractor *Ptr) {
- delete Ptr;
-}
-
-extern EntityId addEntity(PointerFlowTUSummaryExtractor &Extractor,
- EntityName &EN) {
- return Extractor.addEntity(EN);
-}
+};
} // namespace clang::ssaf
// NOLINTNEXTLINE(misc-use-internal-linkage)
volatile int PointerFlowTUSummaryExtractorAnchorSource = 0;
static TUSummaryExtractorRegistry::Add<PointerFlowTUSummaryExtractor>
- RegisterExtractor("PointerFlowTUSummaryExtractor",
+ RegisterExtractor(PointerFlowEntitySummary::Name,
"The TUSummaryExtractor for pointer assignments");
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
index 17c9bf7ba0491..f4f0a12606c72 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -14,6 +14,7 @@
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/Basic/SourceLocation.h"
@@ -82,10 +83,18 @@ class ContributorFinder : public DynamicRecursiveASTVisitor {
bool VisitVarDecl(VarDecl *D) override {
DeclContext *DC = D->getDeclContext();
- if (DC->isFileContext() || DC->isNamespace())
+ // Collects Decl for global variables or static data members:
+ if (DC->isFileContext() || DC->isNamespace() || D->isStaticDataMember())
Contributors.push_back(D);
return true;
}
+
+ bool VisitLambdaExpr(LambdaExpr *L) override {
+ // TraversetLambdaExpr directly visits the body stmt, but we need to collect
+ // the CXXMethodDecl as a contributor:
+ VisitFunctionDecl(L->getCallOperator());
+ return true;
+ }
};
/// An AST visitor that skips the root node's strict-descendants that are
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
index 2a3fb8ef9ee72..d8fab3032d770 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h"
+#include "TestFixture.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
@@ -21,6 +22,7 @@
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <memory>
#include <type_traits>
@@ -29,39 +31,6 @@
using namespace clang;
using namespace ssaf;
-namespace clang::ssaf {
-/////////////////////////////////////////////////////
-// Declare Proxy functions
-/////////////////////////////////////////////////////
-class PointerFlowTUSummaryExtractor;
-
-extern Expected<std::unique_ptr<PointerFlowEntitySummary>>
-extractEntitySummary(PointerFlowTUSummaryExtractor &Extractor,
- const NamedDecl *Contributor, ASTContext &Ctx);
-
-extern PointerFlowTUSummaryExtractor *
-createPointerFlowTUSummaryExtractor(TUSummaryBuilder &Builder);
-
-extern void deletePointerFlowTUSummaryExtractor(
- PointerFlowTUSummaryExtractor *);
-
-extern EntityId addEntity(PointerFlowTUSummaryExtractor &Extractor,
- EntityName &EN);
-
-class PointerFlowTUSummaryExtractorProxy {
- PointerFlowTUSummaryExtractor *Ptr;
-
-public:
- explicit PointerFlowTUSummaryExtractorProxy(TUSummaryBuilder &Builder)
- : Ptr(createPointerFlowTUSummaryExtractor(Builder)) {}
- ~PointerFlowTUSummaryExtractorProxy() {
- deletePointerFlowTUSummaryExtractor(Ptr);
- }
-
- PointerFlowTUSummaryExtractor &operator*() const { return *Ptr; }
-};
-} // namespace clang::ssaf
-
namespace {
// Use FindEntityByName to identify entities in unit tests.
// Unit tests are simple enough to meet the following assumptions:
@@ -100,8 +69,6 @@ StringRef toStringRef(const FindEntityByName &N) {
const NamedDecl *matchNamedDeclByFindEntityByName(const FindEntityByName &N,
const NamedDecl *D) {
- if (std::holds_alternative<LambdaOfVar>(N))
- D->dump();
return std::visit(
Overloaded{
[&D](StringRef S) -> const NamedDecl * {
@@ -119,7 +86,6 @@ const NamedDecl *matchNamedDeclByFindEntityByName(const FindEntityByName &N,
},
[&D](const LambdaOfVar &L) -> const NamedDecl * {
if (const auto *VD = dyn_cast<VarDecl>(D); VD && VD->getInit()) {
- VD->dump();
if (isa<LambdaExpr>(VD->getInit()) &&
VD->getNameAsString() == L.VarName)
return cast<LambdaExpr>(VD->getInit())->getCallOperator();
@@ -175,51 +141,81 @@ struct EPLPair {
bool isFunRet;
};
-class PointerFlowTest : public testing::Test {
+class PointerFlowTest : public TestFixture {
protected:
TUSummary TUSum;
TUSummaryBuilder Builder;
- PointerFlowTUSummaryExtractorProxy ExtractorProxy;
+ std::unique_ptr<TUSummaryExtractor> Extractor;
std::unique_ptr<ASTUnit> AST;
PointerFlowTest()
: TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")),
- Builder(TUSum), ExtractorProxy(Builder) {}
+ Builder(TUSum), Extractor(nullptr) {}
template <typename ContributorDecl = NamedDecl,
typename =
std::enable_if_t<std::is_base_of_v<NamedDecl, ContributorDecl>>>
- std::unique_ptr<PointerFlowEntitySummary>
- setUpTest(StringRef Code, FindEntityByName ContributorName) {
+ bool setUpTest(StringRef Code) {
AST = tooling::buildASTFromCodeWithArgs(
Code, {"-Wno-unused-value", "-Wno-int-to-pointer-cast"});
- const auto *ContributorDefn = findEntityByName<ContributorDecl>(
- ContributorName, AST->getASTContext());
+ for (auto &E : clang::ssaf::TUSummaryExtractorRegistry::entries()) {
+ if (E.getName() == PointerFlowEntitySummary::Name) {
+ Extractor = E.instantiate(Builder);
+ break;
+ }
+ }
- if (!ContributorDefn)
+ if (!Extractor) {
+ ADD_FAILURE() << "failed to find PointerFlowTUSummaryExtractor";
+ return false;
+ }
+ Extractor->HandleTranslationUnit(AST->getASTContext());
+ return true;
+ }
+
+ template <typename ContributorDecl = NamedDecl>
+ const PointerFlowEntitySummary *getEntitySummary(FindEntityByName Name) {
+ const auto *ContributorDefn =
+ findEntityByName<ContributorDecl>(Name, AST->getASTContext());
+
+ if (!ContributorDefn) {
+ ADD_FAILURE() << "failed to find Decl of \"" << toStringRef(Name) << "\"";
return nullptr;
+ }
std::optional<EntityName> EN = getEntityName(ContributorDefn);
- if (!EN)
+ if (!EN) {
+ ADD_FAILURE() << "failed to get EntityName for contributor \""
+ << toStringRef(Name) << "\"";
return nullptr;
+ }
+
+ EntityId ContributorEntityId = Builder.addEntity(*EN);
+ auto &TUSumData = getData(TUSum);
+ auto EntitiesSumIter =
+ TUSumData.find(PointerFlowEntitySummary::summaryName());
- auto Sum = extractEntitySummary(*ExtractorProxy, ContributorDefn,
- AST->getASTContext());
- if (!Sum) {
- llvm::consumeError(std::move(Sum.takeError()));
+ // If none entity summary was collected, it may not be an entry in
+ // `TUSumData`:
+ if (EntitiesSumIter == TUSumData.end())
return nullptr;
- }
- assert(*Sum);
- return std::move(*Sum);
+
+ auto EntitySumIter = EntitiesSumIter->second.find(ContributorEntityId);
+
+ // If entity summary is empty, it may not exist:
+ if (EntitySumIter == EntitiesSumIter->second.end())
+ return nullptr;
+ return static_cast<const PointerFlowEntitySummary *>(
+ EntitySumIter->second.get());
}
public:
std::optional<EntityId> getEntityId(FindEntityByName Name) {
if (const auto *D = findEntityByName(Name, AST->getASTContext())) {
if (auto EntityName = getEntityName(D))
- return addEntity(*ExtractorProxy, *EntityName);
+ return Builder.addEntity(*EntityName);
}
return std::nullopt;
}
@@ -227,7 +223,7 @@ class PointerFlowTest : public testing::Test {
std::optional<EntityId> getEntityIdForReturn(FindEntityByName FunName) {
if (const auto *D = findFnByName(FunName, AST->getASTContext())) {
if (auto EntityName = getEntityNameForReturn(D))
- return addEntity(*ExtractorProxy, *EntityName);
+ return Builder.addEntity(*EntityName);
}
return std::nullopt;
}
@@ -254,7 +250,7 @@ static constexpr auto ToEPL =
EdgeSet
PointerFlowTest::makeEdges(unsigned Line,
- ArrayRef<std::pair<EPLPair, EPLPair>> Edges) {
+ ArrayRef<std::pair<EPLPair, EPLPair>> Edges) {
EdgeSet Result;
for (auto Edge : Edges)
Result[ToEPL(this, Line)(Edge.first)].insert(
@@ -263,69 +259,78 @@ PointerFlowTest::makeEdges(unsigned Line,
}
TEST_F(PointerFlowTest, IsExtractorRegisteredTest) {
- EXPECT_TRUE(
- isTUSummaryExtractorRegistered("PointerFlowTUSummaryExtractor"));
+ EXPECT_TRUE(isTUSummaryExtractorRegistered("PointerFlow"));
}
//////////////////////////////////////////////////////////////
// Simple Assign Tests //
//////////////////////////////////////////////////////////////
TEST_F(PointerFlowTest, SimpleAssign) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p, int *q) {
q = p;
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
}
TEST_F(PointerFlowTest, AssignWithSubscriptLHS) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int **q, int *p, int x) {
q[x] = p;
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 2U}, {"p", 1U}}}));
}
TEST_F(PointerFlowTest, AssignWithPtrArithRHS) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p, int *q) {
q = p + 5;
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
}
TEST_F(PointerFlowTest, AssignInSubscript) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p, int *q) {
(q = p)[5];
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
}
TEST_F(PointerFlowTest, MultipleAssign) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p, int *q, int *r) {
q = p;
r = q;
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -335,12 +340,14 @@ TEST_F(PointerFlowTest, MultipleAssign) {
}
TEST_F(PointerFlowTest, ChainedAssign) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p, int *q, int *r) {
r = q = p;
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -350,38 +357,44 @@ TEST_F(PointerFlowTest, ChainedAssign) {
}
TEST_F(PointerFlowTest, CastToRValue) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p, int *q) {
q = static_cast<int *&&>(p);
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
}
TEST_F(PointerFlowTest, AssignToMember) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct S { int *field; };
void foo(S s, int *p) {
s.field = p;
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"field", 1U}, {"p", 1U}}}));
}
TEST_F(PointerFlowTest, AssignToMember2) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct S { int *field; };
void foo(S *s, int *p) {
s->field = p;
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"field", 1U}, {"p", 1U}}}));
@@ -391,26 +404,30 @@ TEST_F(PointerFlowTest, AssignToMember2) {
// Call Expr Tests. //
//////////////////////////////////////////////////////////////
TEST_F(PointerFlowTest, CallArg) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void bar(int *param);
void foo(int *p) {
bar(p);
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"param", 1U}, {"p", 1U}}}));
}
TEST_F(PointerFlowTest, CallMultiArgs) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void bar(int *param1, int y, int *param2);
void foo(int *p, int x, int *q) {
bar(p, x, q);
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -420,14 +437,16 @@ TEST_F(PointerFlowTest, CallMultiArgs) {
}
TEST_F(PointerFlowTest, CallAsCallArg) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
int *bar(int * w);
void foo(int * p) {
foo(bar(p));
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"w", 1U}, {"p", 1U}},
@@ -435,15 +454,17 @@ TEST_F(PointerFlowTest, CallAsCallArg) {
}
TEST_F(PointerFlowTest, CXXOperatorCallMultiArgs) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct S {
int* operator()(int *a, int *b);
};
void foo(S obj, int *p, int *q) {
foo(obj, obj(p, q), obj(p, q));
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -455,15 +476,17 @@ TEST_F(PointerFlowTest, CXXOperatorCallMultiArgs) {
}
TEST_F(PointerFlowTest, CXXMemberCall) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct S {
int* method(int *a, int *b);
};
void foo(S obj, int *p, int *q) {
foo(obj, obj.method(p, q), obj.method(p, q));
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"a", 1U}, {"p", 1U}},
@@ -473,30 +496,34 @@ TEST_F(PointerFlowTest, CXXMemberCall) {
}
TEST_F(PointerFlowTest, VirtualMethodCall) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct Base {
virtual void method(int *a);
};
void foo(Base &obj, int *p) {
obj.method(p);
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"a", 1U}, {"p", 1U}}}));
}
TEST_F(PointerFlowTest, StaticMethodCall) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct S {
static void method(int *a, int *b);
};
void foo(int *p, int *q) {
S::method(p, q);
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -506,14 +533,16 @@ TEST_F(PointerFlowTest, StaticMethodCall) {
}
TEST_F(PointerFlowTest, DefaultArg) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
int *g;
void bar(int *a, int *b = g);
void foo(int *p) {
bar(p);
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__,
@@ -521,14 +550,16 @@ TEST_F(PointerFlowTest, DefaultArg) {
}
TEST_F(PointerFlowTest, DefaultArg2) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
int *g;
void bar(int *a, int *b = g);
void foo(int *p) {
bar(p, p);
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -541,15 +572,17 @@ TEST_F(PointerFlowTest, DefaultArg2) {
// CXX Ctor Tests. //
//////////////////////////////////////////////////////////////
TEST_F(PointerFlowTest, CXXCtorCallMultiArgs) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct S {
S(int *a, int *b) {}
};
void foo(int *p, int *q) {
S s{p, q};
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -559,15 +592,17 @@ TEST_F(PointerFlowTest, CXXCtorCallMultiArgs) {
}
TEST_F(PointerFlowTest, CXXCtorCallMultiArgs2) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct S {
S(int *a, int x, int *b) {}
};
void foo(int *p, int x, int *q) {
S s{p, x, q};
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -577,7 +612,7 @@ TEST_F(PointerFlowTest, CXXCtorCallMultiArgs2) {
}
TEST_F(PointerFlowTest, CXXCtorCallAsCallArg) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct Wrapper {
Wrapper(int *q) {}
};
@@ -585,21 +620,25 @@ TEST_F(PointerFlowTest, CXXCtorCallAsCallArg) {
void foo(int *p) {
bar(Wrapper{p});
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
}
TEST_F(PointerFlowTest, DelegatingCXXCtorCall) {
- auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct S {
S(int *a, int *b) {}
S(int *p) : S(p, p) {}
};
- )cpp",
- CXXCtorOfNumParms{"S", 1});
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary<CXXConstructorDecl>(CXXCtorOfNumParms{"S", 1});
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -609,15 +648,17 @@ TEST_F(PointerFlowTest, DelegatingCXXCtorCall) {
}
TEST_F(PointerFlowTest, CXXCtorBaseInit) {
- auto Sum = setUpTest<CXXConstructorDecl>(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct Base {
Base(int *a) {}
};
struct Derived : Base {
Derived(int *p) : Base(p) {}
};
- )cpp",
- "Derived");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary<CXXConstructorDecl>("Derived");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"a", 1U}, {"p", 1U}}}));
@@ -627,38 +668,44 @@ TEST_F(PointerFlowTest, CXXCtorBaseInit) {
// Initializers Tests. //
//////////////////////////////////////////////////////////////
TEST_F(PointerFlowTest, LocalVarDeclInit) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p) {
int *q = p;
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
}
TEST_F(PointerFlowTest, LocalVarDeclInit2) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int (*arr)[10]) {
int (*p)[10] = arr;
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"p", 1U}, {"arr", 1U}}}));
}
TEST_F(PointerFlowTest, FieldInit) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p) {
struct Bar {
int *field = p;
};
}
- )cpp",
- "Bar");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("Bar");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"field", 1U}, {"p", 1U}}}));
@@ -675,46 +722,54 @@ TEST_F(PointerFlowTest, CXXCtorMemberInit) {
}
)cpp";
- auto Sum = setUpTest<CXXConstructorDecl>(Code, "Bar");
+ ASSERT_EQ(setUpTest(Code), true);
+ auto *Sum = getEntitySummary<CXXConstructorDecl>("Bar");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"member", 1U}, {"q", 1U}}}));
- Sum = setUpTest(Code, "foo");
+ Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"q", 1U}, {"p", 1U}}}));
}
TEST_F(PointerFlowTest, GlobalVarInit) {
- auto Sum = setUpTest<VarDecl>(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
int *q;
int *g = q;
- )cpp",
- "g");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary<VarDecl>("g");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"g", 1U}, {"q", 1U}}}));
}
TEST_F(PointerFlowTest, StaticLocalInit) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p) {
static int *s = p;
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"s", 1U}, {"p", 1U}}}));
}
TEST_F(PointerFlowTest, StaticMemberInit) {
- auto Sum = setUpTest<VarDecl>(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
int *g;
- struct S { static int *member = g; };
- )cpp",
- "member");
+ struct S { static int *member; };
+ int *S::member = g;
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary<VarDecl>("member");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"member", 1U}, {"g", 1U}}}));
@@ -725,12 +780,14 @@ TEST_F(PointerFlowTest, StaticMemberInit) {
//////////////////////////////////////////////////////////////
TEST_F(PointerFlowTest, ArrayInitList) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p, int *q) {
int *arr[] = {p, q};
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -740,13 +797,15 @@ TEST_F(PointerFlowTest, ArrayInitList) {
}
TEST_F(PointerFlowTest, StructInitList) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct S { int *a; int *b; };
void foo(int *p, int *q) {
S s = {p, q};
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -757,27 +816,31 @@ TEST_F(PointerFlowTest, StructInitList) {
// A union initialized with a brace-enclosed initializer:
TEST_F(PointerFlowTest, UnionInitList) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
union U { int *x; int y; };
void foo(int *p) {
U u = {p};
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"x", 1U}, {"p", 1U}}}));
}
TEST_F(PointerFlowTest, NestedInitList) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct Inner { int * a; int * b; };
struct S { Inner c; int * d; };
void foo(int *p, int *q, int *r) {
S s = {{p, q}, r};
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -788,14 +851,16 @@ TEST_F(PointerFlowTest, NestedInitList) {
}
TEST_F(PointerFlowTest, NestedInitList2) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
union Inner { int * a; int b; };
struct S { Inner c; int * d; };
void foo(int *p, int *q) {
S s = {{p}, q};
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -805,14 +870,16 @@ TEST_F(PointerFlowTest, NestedInitList2) {
}
TEST_F(PointerFlowTest, NestedInitList3) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct Inner { int * a; int * b; };
union S { Inner c; int * d; };
void foo(int *p, int *q) {
S s = {{p, q}};
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -822,12 +889,14 @@ TEST_F(PointerFlowTest, NestedInitList3) {
}
TEST_F(PointerFlowTest, NestedArrayInitList) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo(int *p, int *q, int *r, int *s) {
int *arr[][2] = {{p, q}, {r, s}};
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -839,13 +908,15 @@ TEST_F(PointerFlowTest, NestedArrayInitList) {
}
TEST_F(PointerFlowTest, MixedNestedArrayStructInitList) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct T { int *arr[2]; };
void foo(int *p, int *q, int *r, int *s) {
T t[2] = {{p, q}, {r, s}};
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -857,13 +928,15 @@ TEST_F(PointerFlowTest, MixedNestedArrayStructInitList) {
}
TEST_F(PointerFlowTest, ArrayOfStructInitList) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
struct S { int *a; int *b; };
void foo(int *p, int *q, int *r, int *s) {
S arr[] = {{p, q}, {r, s}};
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -879,26 +952,30 @@ TEST_F(PointerFlowTest, ArrayOfStructInitList) {
//////////////////////////////////////////////////////////////
TEST_F(PointerFlowTest, ReturnEdge) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
int *foo(int *p) {
return p;
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"foo", 1U, true}, {"p", 1U}}}));
}
TEST_F(PointerFlowTest, MultipleReturnEdges) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
int *foo(int *p, int *q, bool cond) {
if (cond)
return p;
return q;
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {
@@ -908,19 +985,21 @@ TEST_F(PointerFlowTest, MultipleReturnEdges) {
}
TEST_F(PointerFlowTest, NoReturnEdgeForNonPointerReturnType) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
int foo(int *p, int x) {
return x;
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
- ASSERT_NE(Sum, nullptr);
- EXPECT_EQ(*Sum, makeEdges(__LINE__, {}));
+ auto *Sum = getEntitySummary("foo");
+
+ EXPECT_THAT(Sum, testing::AnyOf(testing::IsNull(),
+ testing::Pointee(makeEdges(__LINE__, {}))));
}
TEST_F(PointerFlowTest, ReturnEdgeNotFromNestedFunction) {
- auto *Code = R"cpp(
+ StringRef Code = R"cpp(
int *foo(int *p) {
struct Inner {
int *bar(int *q) { return q; }
@@ -928,54 +1007,58 @@ TEST_F(PointerFlowTest, ReturnEdgeNotFromNestedFunction) {
return p;
}
)cpp";
- auto Sum = setUpTest(Code, "foo");
+
+ ASSERT_EQ(setUpTest(Code), true);
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"foo", 1U, true}, {"p", 1U}}}));
- Sum = setUpTest(Code, "bar");
+ Sum = getEntitySummary("bar");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"bar", 1U, true}, {"q", 1U}}}));
}
TEST_F(PointerFlowTest, ReturnEdgeInClassMethod) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void foo() {
struct S {
int *method(int *p, int *q) { return p; }
};
}
- )cpp",
- "method");
+ )cpp"),
+ true);
+
+ auto *Sum = getEntitySummary("method");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"method", 1U, true}, {"p", 1U}}}));
}
TEST_F(PointerFlowTest, NoEdgeFromIndirectCall) {
- auto Sum = setUpTest(R"cpp(
+ ASSERT_EQ(setUpTest(R"cpp(
void bar(int *param1);
void baz(int *param2);
-
+
void foo(int *p, void (*fp)(int *)) {
fp(p);
}
- void main() {
+ int main() {
int *q;
foo(q, bar);
foo(q, baz);
+ return 0;
}
- )cpp",
- "foo");
+ )cpp"),
+ true);
- ASSERT_NE(Sum, nullptr);
- EXPECT_EQ(
- *Sum,
- makeEdges(
- __LINE__,
- /* FIXME or TBD: Currently indirect calls produce no edge: */ {}));
+ /* FIXME or TBD: Currently indirect calls produce no edge: */
+ auto *Sum = getEntitySummary("foo");
+
+ EXPECT_THAT(Sum, testing::AnyOf(testing::IsNull(),
+ testing::Pointee(makeEdges(__LINE__, {}))));
}
//////////////////////////////////////////////////////////////
@@ -990,14 +1073,15 @@ TEST_F(PointerFlowTest, ReturnInLambda) {
}
)cpp";
- auto Sum = setUpTest(Code, "foo");
+ ASSERT_EQ(setUpTest(Code), true);
+ auto *Sum = getEntitySummary("foo");
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"r", 1U}, {"p", 1U}},
{{"foo", 1U, true},
{LambdaOfVar{"local"}, 1U, true}}}));
- Sum = setUpTest(Code, LambdaOfVar{"local"});
+ Sum = getEntitySummary(LambdaOfVar{"local"});
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__,
{{{LambdaOfVar{"local"}, 1U, true}, {"r", 1U}}}));
@@ -1013,11 +1097,12 @@ TEST_F(PointerFlowTest, NestedLambdaAssign) {
}
)cpp";
- auto Sum = setUpTest(Code, LambdaOfVar{"outer_lambda"});
+ ASSERT_EQ(setUpTest(Code), true);
+ auto *Sum = getEntitySummary(LambdaOfVar{"outer_lambda"});
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"s", 1U}, {"r", 1U}}}));
- Sum = setUpTest(Code, LambdaOfVar{"inner_lambda"});
+ Sum = getEntitySummary(LambdaOfVar{"inner_lambda"});
ASSERT_NE(Sum, nullptr);
EXPECT_EQ(*Sum, makeEdges(__LINE__, {{{"y", 1U}, {"x", 1U}}}));
}
>From 33209adc6f4a6574ccb3260959b6934ce1ff110a Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 8 Apr 2026 15:16:50 -0700
Subject: [PATCH 44/51] add include of ExprCXX.h
---
.../Analyses/SSAFAnalysesCommon.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
index f4f0a12606c72..93d2ea6bad4de 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -17,6 +17,7 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Twine.h"
>From c52d2df094b55d27161c5a0019c1217ae909abd6 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 8 Apr 2026 15:20:18 -0700
Subject: [PATCH 45/51] fix clang-format
---
.../Analyses/EntityPointerLevel/EntityPointerLevel.h | 4 ++--
.../Analyses/SSAFAnalysesCommon.h | 1 -
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
index 56ae2e4fd5f36..7ebfa4788c180 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
@@ -108,8 +108,8 @@ translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
/// of a function entity.
llvm::Expected<EntityPointerLevel>
createEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx,
- std::function<EntityId(EntityName EN)> AddEntity,
- bool IsFunRet = false);
+ std::function<EntityId(EntityName EN)> AddEntity,
+ bool IsFunRet = false);
/// Creates a `EntityPointerLevel` from a pair of an EntityId and a pointer
/// level:
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
index 93d2ea6bad4de..c57579c71a36e 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -156,4 +156,3 @@ class ContributorFactFinder : public DynamicRecursiveASTVisitor {
};
} // namespace clang::ssaf
#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
-
>From be1be133dc1f41161ba0a1e498161820c0839db2 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 8 Apr 2026 16:31:01 -0700
Subject: [PATCH 46/51] fix a test failure on Windows
---
.../Analyses/PointerFlow/PointerFlowTest.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
index d8fab3032d770..0b8df1c0d5e75 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
@@ -86,9 +86,10 @@ const NamedDecl *matchNamedDeclByFindEntityByName(const FindEntityByName &N,
},
[&D](const LambdaOfVar &L) -> const NamedDecl * {
if (const auto *VD = dyn_cast<VarDecl>(D); VD && VD->getInit()) {
- if (isa<LambdaExpr>(VD->getInit()) &&
+ const Expr *Init = VD->getInit()->IgnoreUnlessSpelledInSource();
+ if (isa<LambdaExpr>(Init) &&
VD->getNameAsString() == L.VarName)
- return cast<LambdaExpr>(VD->getInit())->getCallOperator();
+ return cast<LambdaExpr>(Init)->getCallOperator();
}
return nullptr;
},
>From 6cd1636ea8dc7a7291f93e047796957c7cad43d8 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 8 Apr 2026 17:02:47 -0700
Subject: [PATCH 47/51] fix clang-format
---
.../Analyses/PointerFlow/PointerFlowTest.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
index 0b8df1c0d5e75..445506c8af6b7 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowTest.cpp
@@ -87,8 +87,7 @@ const NamedDecl *matchNamedDeclByFindEntityByName(const FindEntityByName &N,
[&D](const LambdaOfVar &L) -> const NamedDecl * {
if (const auto *VD = dyn_cast<VarDecl>(D); VD && VD->getInit()) {
const Expr *Init = VD->getInit()->IgnoreUnlessSpelledInSource();
- if (isa<LambdaExpr>(Init) &&
- VD->getNameAsString() == L.VarName)
+ if (isa<LambdaExpr>(Init) && VD->getNameAsString() == L.VarName)
return cast<LambdaExpr>(Init)->getCallOperator();
}
return nullptr;
>From 477feef3e18e57a9484bda88c7bb8d13cc35955d Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 15 Apr 2026 10:36:51 -0700
Subject: [PATCH 48/51] Fix issues after merge
---
.../EntityPointerLevel/EntityPointerLevel.h | 2 +-
clang/lib/Analysis/UnsafeBufferUsage.cpp | 6 ---
.../EntityPointerLevel/EntityPointerLevel.cpp | 8 ++--
.../PointerFlow/PointerFlowExtractor.cpp | 39 +++++++++--------
.../Analyses/SSAFAnalysesCommon.cpp | 10 ++++-
.../UnsafeBufferUsageExtractor.cpp | 2 +-
.../UnsafeBufferUsage/Inputs/tu-summary.json | 43 -------------------
.../tu-summary-serialization.test | 14 ------
.../Analysis/Scalable/ssaf-format/list.test | 7 +--
9 files changed, 36 insertions(+), 95 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
index 0e120a40eebb8..3302ef6e2d06b 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h
@@ -109,7 +109,7 @@ EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
/// \param IsFunRet true iff the created EPL is associated with the return type
/// of a function entity.
llvm::Expected<EntityPointerLevel>
-creatEntityPointerLevel(const NamedDecl *ND,
+createEntityPointerLevel(const NamedDecl *ND,
llvm::function_ref<EntityId(EntityName EN)> AddEntity,
bool IsFunRet = false);
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index 82503cf34041b..e3ccf1ddd5e02 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -2982,10 +2982,7 @@ static bool populateStmtsForFindingGadgets(SmallVector<const Stmt *> &Stmts,
AddStmt(FD->getInClassInitializer());
return true;
}
-<<<<<<< HEAD
return false;
-=======
->>>>>>> users/ziqingluo/PR-172429193-2-split-4
}
struct WarningGadgetSets {
@@ -4750,15 +4747,12 @@ bool clang::matchUnsafePointers(const DynTypedNode &N, ASTContext &Ctx,
WarningGadgetList WarningGadgets;
bool Matched = false;
-<<<<<<< HEAD
-=======
// FIXME: By design, we don't need MockReporter, and we are supposed to
// only define WARNING_GADGET when we want to treat WARNING_OPTIONAL_GADGET
// the same as WARNING_GADGET. The reason we have to do it this way now is
// that some WARNING_OPTIONAL_GADGETs do not have the 3-argument `matches`
// overload. We need to fix this problem in a separate patch.
->>>>>>> users/ziqingluo/PR-172429193-2-split-4
#define WARNING_GADGET(name) \
if (name##Gadget::matches(S, Ctx, Result)) \
WarningGadgets.push_back(std::make_unique<name##Gadget>(Result));
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
index af0f9dcb6652a..98b93455ac427 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp
@@ -68,12 +68,12 @@ class EntityPointerLevelTranslator
return EntityPointerLevelSet{Incremented.begin(), Incremented.end()};
}
- std::function<EntityId(EntityName EN)> AddEntity;
+ llvm::function_ref<EntityId(EntityName EN)> AddEntity;
ASTContext &Ctx;
public:
- EntityPointerLevelTranslator(std::function<EntityId(EntityName EN)> AddEntity,
- ASTContext &Ctx)
+ EntityPointerLevelTranslator(
+ llvm::function_ref<EntityId(EntityName EN)> AddEntity, ASTContext &Ctx)
: AddEntity(AddEntity), Ctx(Ctx) {}
Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
@@ -244,7 +244,7 @@ Expected<EntityPointerLevelSet> clang::ssaf::translateEntityPointerLevel(
}
/// Create an EntityPointerLevel from a ValueDecl of a pointer type.
-Expected<EntityPointerLevel> clang::ssaf::creatEntityPointerLevel(
+Expected<EntityPointerLevel> clang::ssaf::createEntityPointerLevel(
const NamedDecl *ND, llvm::function_ref<EntityId(EntityName EN)> AddEntity,
bool IsFunRet) {
EntityPointerLevelTranslator Translator(AddEntity, ND->getASTContext());
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
index 16eeb70dfe25c..ad67d04bd8f19 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
@@ -24,8 +24,8 @@
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/Support/Error.h"
-#include <functional>
#include <memory>
#include <optional>
@@ -40,7 +40,7 @@ class PointerAssignmentMatcher {
// Convert a Expr/NamedDecl to an EntityPointerLevel(Set):
Expected<EntityPointerLevelSet> toEPL(const NamedDecl *N,
bool IsRet = false) {
- auto Ret = createEntityPointerLevel(N, Ctx, AddEntity, IsRet);
+ auto Ret = createEntityPointerLevel(N, AddEntity, IsRet);
if (Ret)
return EntityPointerLevelSet{*Ret};
@@ -86,7 +86,7 @@ class PointerAssignmentMatcher {
for (unsigned ParmIdx = 0; ParmIdx < PP->getNumParams();
++ArgIdx, ++ParmIdx) {
if (const ParmVarDecl *PD = PP->getParamDecl(ParmIdx);
- PD && hasPtrOrArrType(*PD)) {
+ PD && hasPtrOrArrType(PD)) {
addEdges(PD, AP->getArg(ArgIdx));
Matched = true;
}
@@ -116,7 +116,7 @@ class PointerAssignmentMatcher {
const InitListExpr *ILE = dyn_cast<InitListExpr>(InitExpr);
if (!ILE) {
- if (!hasPtrOrArrType(*InitExpr))
+ if (!hasPtrOrArrType(InitExpr))
return false;
auto Expected = toEPL(Base);
@@ -154,7 +154,7 @@ class PointerAssignmentMatcher {
if (CXXRD->getNumBases() != 0) {
// FIXME: support this:
addError(makeErrAtNode(
- Ctx, *ILE,
+ Ctx, ILE,
"attempt to create pointer assignment edges between "
"CXXRecordDecls with base classes and initializer-lists"));
return false;
@@ -215,12 +215,11 @@ class PointerAssignmentMatcher {
// ctor's body will be visited later.
// XF(T var = e) => XF(Var = e)
// XF(T var = init-list) => see `matchInitializerList`
- bool matches(const DynTypedNode &DynNode, ASTContext &Ctx,
- const NamedDecl *RootDecl) {
+ bool matches(const DynTypedNode &DynNode, const NamedDecl *RootDecl) {
if (const Stmt *S = DynNode.get<Stmt>()) {
// Match 'p = q' whenever it has pointer or array type:
if (const auto *BO = dyn_cast<BinaryOperator>(S);
- BO && BO->getOpcode() == BO_Assign && hasPtrOrArrType(*BO)) {
+ BO && BO->getOpcode() == BO_Assign && hasPtrOrArrType(BO)) {
return addEdges(BO->getLHS(), BO->getRHS());
}
@@ -247,7 +246,7 @@ class PointerAssignmentMatcher {
}
if (const auto *RS = dyn_cast<ReturnStmt>(S)) {
const Expr *RetExpr = RS->getRetValue();
- if (!hasPtrOrArrType(*RetExpr))
+ if (!hasPtrOrArrType(RetExpr))
return false;
return addEdges(toEPL(RootDecl, true), RetExpr, false);
}
@@ -267,7 +266,7 @@ class PointerAssignmentMatcher {
return matchInitializerList(VD, InitLst);
// Match initializers to variables/fields of a pointer type:
- if (InitExpr && hasPtrOrArrType(*VD))
+ if (InitExpr && hasPtrOrArrType(VD))
return addEdges(VD, InitExpr);
}
@@ -277,9 +276,8 @@ class PointerAssignmentMatcher {
for (auto *E : CtorD->inits()) {
if (E->isDelegatingInitializer())
- return matches(DynTypedNode::create(*E->getInit()), Ctx, RootDecl);
- if (const FieldDecl *FD = E->getMember();
- FD && hasPtrOrArrType(*FD)) {
+ return matches(DynTypedNode::create(*E->getInit()), RootDecl);
+ if (const FieldDecl *FD = E->getMember(); FD && hasPtrOrArrType(FD)) {
addEdges(E->getMember(), E->getInit());
Matched = true;
}
@@ -306,9 +304,11 @@ class PointerFlowTUSummaryExtractor : public TUSummaryExtractor {
extractEntitySummary(const NamedDecl *Contributor, ASTContext &Ctx) {
PointerAssignmentMatcher Matcher(
Ctx, [this](const EntityName &EN) { return addEntity(EN); });
- ContributorFactFinder<PointerAssignmentMatcher> Finder(Ctx, Matcher);
+ auto MatchAction = [&Matcher, &Contributor](const DynTypedNode &Node) {
+ Matcher.matches(Node, Contributor);
+ };
- Finder.findMatches(const_cast<NamedDecl *>(Contributor));
+ findMatchesIn(Contributor, MatchAction);
if (Matcher.Error)
return std::move(Matcher.Error);
return std::make_unique<PointerFlowEntitySummary>(
@@ -316,10 +316,10 @@ class PointerFlowTUSummaryExtractor : public TUSummaryExtractor {
}
void HandleTranslationUnit(ASTContext &Ctx) override {
- ContributorFinder ContributorFinder;
+ std::vector<const NamedDecl *> Contributors;
- ContributorFinder.TraverseAST(Ctx);
- for (auto *CD : ContributorFinder.Contributors) {
+ findContributors(Ctx, Contributors);
+ for (auto *CD : Contributors) {
auto EntitySummary = extractEntitySummary(CD, Ctx);
if (!EntitySummary)
@@ -331,7 +331,7 @@ class PointerFlowTUSummaryExtractor : public TUSummaryExtractor {
auto ContributorName = getEntityName(CD);
if (!ContributorName)
- llvm::reportFatalInternalError(makeEntityNameErr(Ctx, *CD));
+ llvm::reportFatalInternalError(makeEntityNameErr(Ctx, CD));
auto [Ignored, InsertionSucceeded] = SummaryBuilder.addSummary(
addEntity(*ContributorName), std::move(*EntitySummary));
@@ -344,6 +344,7 @@ class PointerFlowTUSummaryExtractor : public TUSummaryExtractor {
// NOLINTNEXTLINE(misc-use-internal-linkage)
volatile int PointerFlowTUSummaryExtractorAnchorSource = 0;
+
static TUSummaryExtractorRegistry::Add<PointerFlowTUSummaryExtractor>
RegisterExtractor(PointerFlowEntitySummary::Name,
"The TUSummaryExtractor for pointer assignments");
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.cpp
index 31962bdee7f92..cf85e4c62c89f 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.cpp
@@ -27,10 +27,18 @@ class ContributorFinder : public DynamicRecursiveASTVisitor {
bool VisitVarDecl(VarDecl *D) override {
DeclContext *DC = D->getDeclContext();
- if (DC->isFileContext() || DC->isNamespace())
+ // Collects Decl for global variables or static data members:
+ if (DC->isFileContext() || DC->isNamespace() || D->isStaticDataMember())
Contributors.push_back(D);
return true;
}
+
+ bool VisitLambdaExpr(LambdaExpr *L) override {
+ // TraversetLambdaExpr directly visits the body stmt, but we need to collect
+ // the CXXMethodDecl as a contributor:
+ VisitFunctionDecl(L->getCallOperator());
+ return true;
+ }
};
// An AST visitor that skips the root node's strict-descendants that are
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
index 05afc341c79d0..12f13d636283d 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp
@@ -39,7 +39,7 @@ class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor {
std::set<const Expr *> UnsafePointers;
auto MatchAction = [&Ctx, &UnsafePointers](const DynTypedNode &Node) {
- return matchUnsafePointers(Node, Ctx, UnsafePointers);
+ matchUnsafePointers(Node, Ctx, UnsafePointers);
};
findMatchesIn(Contributor, MatchAction);
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary.json b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary.json
index bf26e4c21c56f..382b294ccc7bd 100644
--- a/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary.json
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/Inputs/tu-summary.json
@@ -5,49 +5,6 @@
{
"entity_id": 2,
"entity_summary": {
-<<<<<<< HEAD
- "PointerFlow": [
- [
- [
- {
- "@": 0
- },
- 2
- ],
- [
- {
- "@": 1
- },
- 1
- ]
- ],
- [
- [
- {
- "@": 2
- },
- 1
- ],
- [
- {
- "@": 0
- },
- 1
- ]
- ]
- ]
- }
- }
- ],
- "summary_name": "PointerFlow"
- },
- {
- "summary_data": [
- {
- "entity_id": 2,
- "entity_summary": {
-=======
->>>>>>> users/ziqingluo/PR-172429193-2-split-4
"UnsafeBuffers": [
[
{
diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
index f4be311f7a610..ff13aa7f8ca27 100644
--- a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
+++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test
@@ -5,19 +5,6 @@
// Negative tests:
// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-no-key.json 2>&1 \
-<<<<<<< HEAD
-// RUN: | FileCheck %s --check-prefix=CHECK-NO-KEY
-// CHECK-NO-KEY: saw {
-// CHECK-NO-KEY: "NotUnsafeBuffers": [
-// CHECK-NO-KEY: [
-// CHECK-NO-KEY: {
-// CHECK-NO-KEY: "@": 0
-// CHECK-NO-KEY: },
-// CHECK-NO-KEY: 1
-// CHECK-NO-KEY: ]
-// CHECK-NO-KEY: ]
-// CHECK-NO-KEY: } but expected an Object with a key UnsafeBuffers
-=======
// RUN: | FileCheck %s --check-prefix=CHECK-MISSING-KEY
// CHECK-MISSING-KEY: saw {
// CHECK-MISSING-KEY-NEXT: "NotUnsafeBuffers": [
@@ -29,7 +16,6 @@
// CHECK-MISSING-KEY-NEXT: ]
// CHECK-MISSING-KEY-NEXT: ]
// CHECK-MISSING-KEY-NEXT: } but expected an Object with a key UnsafeBuffers
->>>>>>> users/ziqingluo/PR-172429193-2-split-4
// 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/ssaf-format/list.test b/clang/test/Analysis/Scalable/ssaf-format/list.test
index 1b3fa2e09e8bd..34181845ba3b5 100644
--- a/clang/test/Analysis/Scalable/ssaf-format/list.test
+++ b/clang/test/Analysis/Scalable/ssaf-format/list.test
@@ -7,11 +7,6 @@
// CHECK-EMPTY:
// CHECK-DAG: [[NthFormat:[0-9]+]]. json - JSON serialization format
// CHECK-DAG: Analyses:
-<<<<<<< HEAD
-// CHECK-DAG: [[NthFormat]].[[MthSummary:[0-9]+]]. CallGraph - JSON Format info for CallGraph summary
-// CHECK-DAG: [[NthFormat]].[[MthSummary:[0-9]+]]. UnsafeBufferUsage - JSON Format info for UnsafeBufferUsageEntitySummary
-// CHECK-DAG: [[NthFormat]].[[MthSummary:[0-9]+]]. PointerFlow - JSON Format info for PointerFlowEntitySummary
-=======
// CHECK-DAG: [[NthFormat]].{{[0-9]+}}. CallGraph - JSON Format info for CallGraph summary
// CHECK-DAG: [[NthFormat]].{{[0-9]+}}. UnsafeBufferUsage - JSON Format info for UnsafeBufferUsageEntitySummary
->>>>>>> users/ziqingluo/PR-172429193-2-split-4
+// CHECK-DAG: [[NthFormat]].{{[0-9]+}}. PointerFlow - JSON Format info for PointerFlowEntitySummary
>From 9f8c1d4132e7ab9a208ea5f0a57cc99bdc05f4d1 Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 15 Apr 2026 14:43:45 -0700
Subject: [PATCH 49/51] remove irrelevant changes
---
.../EntityPointerLevelFormat.h | 1 +
clang/lib/Analysis/UnsafeBufferUsage.cpp | 20 +++++--------------
.../Analyses/PointerFlow/PointerFlow.cpp | 2 +-
3 files changed, 7 insertions(+), 16 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
index 2dd8d915d23b4..7b125edb1fb7f 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h
@@ -8,6 +8,7 @@
#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
+
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp
index e3ccf1ddd5e02..e80dfc82e60ba 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -2951,9 +2951,7 @@ template <typename NodeTy> struct CompareNode {
// FunctionDecl
// BlockDecl
// ObjCMethodDecl
-//
-// return false iff `D` is not any one above.
-static bool populateStmtsForFindingGadgets(SmallVector<const Stmt *> &Stmts,
+static void populateStmtsForFindingGadgets(SmallVector<const Stmt *> &Stmts,
const Decl *D) {
auto AddStmt = [&Stmts](const Stmt *S) {
if (S)
@@ -2968,21 +2966,13 @@ static bool populateStmtsForFindingGadgets(SmallVector<const Stmt *> &Stmts,
llvm::append_range(
Stmts, llvm::map_range(CtorD->inits(),
std::mem_fn(&CXXCtorInitializer::getInit)));
- return true;
- }
- if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
+ } else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
AddStmt(D->getBody());
- return true;
- }
- if (const auto *VD = dyn_cast<VarDecl>(D)) {
- AddStmt(VD->getInit());
- return true;
- }
- if (const auto *FD = dyn_cast<FieldDecl>(D)) {
+ } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
+ AddStmt(VD->getInit()); // FIXME: default arg for ParmVarDecl?
+ } else if (const auto *FD = dyn_cast<FieldDecl>(D)) {
AddStmt(FD->getInClassInitializer());
- return true;
}
- return false;
}
struct WarningGadgetSets {
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
index 1ca731e4f58ac..674635879c966 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
@@ -115,4 +115,4 @@ static llvm::Registry<JSONFormat::FormatInfo>::Add<PointerFlowJSONFormatInfo>
"PointerFlow", "JSON Format info for PointerFlowEntitySummary");
// NOLINTNEXTLINE(misc-use-internal-linkage)
-volatile int PointerFlowSSAFJSONFormatAnchorSource = 0;
\ No newline at end of file
+volatile int PointerFlowSSAFJSONFormatAnchorSource = 0;
>From 8d5a7199308607fdc06d4f771c4adc8fb5f44d6b Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Wed, 15 Apr 2026 16:00:01 -0700
Subject: [PATCH 50/51] clean up code
---
.../Analyses/PointerFlow/PointerFlow.h | 2 +-
.../Analyses/PointerFlow/PointerFlow.cpp | 29 ++---
.../PointerFlow/PointerFlowExtractor.cpp | 111 +++++++-----------
.../Analyses/SSAFAnalysesCommon.cpp | 2 +-
.../PointerFlow/tu-summary-serialization.test | 2 +-
5 files changed, 63 insertions(+), 83 deletions(-)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
index b93decd701f05..86c976c48a2e3 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.h
@@ -44,7 +44,7 @@ class PointerFlowEntitySummary final : public EntitySummary {
return Edges == Other.Edges;
}
- bool empty() const { return Edges.empty() && Edges.empty(); }
+ bool empty() const { return Edges.empty(); }
static SummaryName summaryName() { return SummaryName{Name.str()}; }
};
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
index 674635879c966..dc347f4dc4086 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlow.cpp
@@ -8,9 +8,11 @@
#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/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"
@@ -77,7 +79,7 @@ summaryFromJSON(const Object &Data, EntityIdTable &,
if (!EdgesDataAsArr)
return makeSawButExpectedError(
- *EdgesData, "a JSON array of arary of EntityPointerLevels");
+ *EdgesData, "a JSON array of array of EntityPointerLevels");
for (const auto &EdgesEntryData : *EdgesDataAsArr) {
const auto *EPLArray = EdgesEntryData.getAsArray();
@@ -86,19 +88,18 @@ summaryFromJSON(const Object &Data, EntityIdTable &,
EdgesEntryData, "a JSON array of EntityPointerLevels with a size "
"greater than 1: [lhs, rhs, rhs, ...]");
- llvm::Error Err = llvm::Error::success();
- auto EPLs = llvm::map_range(
- *EPLArray, [&Err, &EntityIdFromJSON](const auto &EPLData) {
- auto EPL = entityPointerLevelFromJSON(EPLData, EntityIdFromJSON);
-
- if (!EPL)
- Err = llvm::joinErrors(std::move(Err), EPL.takeError());
- return *EPL;
- });
-
- if (Err)
- return Err;
- Edges[*EPLs.begin()].insert(EPLs.begin() + 1, EPLs.end());
+ auto SrcEPL =
+ entityPointerLevelFromJSON(*EPLArray->begin(), EntityIdFromJSON);
+
+ if (!SrcEPL)
+ return SrcEPL.takeError();
+ for (const auto &EPLData :
+ llvm::make_range(EPLArray->begin() + 1, EPLArray->end())) {
+ auto EPL = entityPointerLevelFromJSON(EPLData, EntityIdFromJSON);
+ if (!EPL)
+ return EPL.takeError();
+ Edges[*SrcEPL].insert(*EPL);
+ }
}
return std::make_unique<PointerFlowEntitySummary>(
buildPointerFlowEntitySummary(std::move(Edges)));
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
index ad67d04bd8f19..8b892c266281c 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/PointerFlow/PointerFlowExtractor.cpp
@@ -52,50 +52,38 @@ class PointerAssignmentMatcher {
}
template <typename T1, typename T2>
- bool addEdges(const T1 *LHS, const T2 *RHS) {
+ llvm::Error addEdges(const T1 *LHS, const T2 *RHS) {
return addEdges(toEPL(LHS), RHS, false);
}
template <typename T>
- bool addEdges(Expected<EntityPointerLevelSet> &&LHS, const T *RHS,
- bool IsRHSRet = false) {
+ llvm::Error addEdges(Expected<EntityPointerLevelSet> &&LHS, const T *RHS,
+ bool IsRHSRet = false) {
auto Rs = toEPL(RHS, IsRHSRet);
- bool Error = false;
- if (!Rs) {
- addError(Rs.takeError());
- Error = true;
- }
- if (!LHS) {
- addError(LHS.takeError());
- Error = true;
- }
- if (Error)
- return false;
+ if (!Rs)
+ return Rs.takeError();
+ if (!LHS)
+ return LHS.takeError();
for (auto L : *LHS)
Results[L].insert(Rs->begin(), Rs->end());
- return true;
+ return llvm::Error::success();
}
template <typename ParmsProvider, typename ArgsProvider>
- bool matchArgsWithParams(unsigned ArgIdxStart, ParmsProvider *PP,
- ArgsProvider *AP) {
- bool Matched = false;
+ llvm::Error matchArgsWithParams(unsigned ArgIdxStart, ParmsProvider *PP,
+ ArgsProvider *AP) {
unsigned ArgIdx = ArgIdxStart;
for (unsigned ParmIdx = 0; ParmIdx < PP->getNumParams();
++ArgIdx, ++ParmIdx) {
if (const ParmVarDecl *PD = PP->getParamDecl(ParmIdx);
PD && hasPtrOrArrType(PD)) {
- addEdges(PD, AP->getArg(ArgIdx));
- Matched = true;
+ if (auto Err = addEdges(PD, AP->getArg(ArgIdx)))
+ return Err;
}
}
- return Matched;
- }
-
- void addError(llvm::Error &&E) {
- Error = llvm::joinErrors(std::move(Error), std::move(E));
+ return llvm::Error::success();
}
// Match initializer lists of the form 'Var = {a, b, c, ...}':
@@ -111,20 +99,18 @@ class PointerAssignmentMatcher {
//
// The process is recursive: 'a', 'b', 'c', etc. may themselves be
// initializer lists. We therefore use `ArrayIndirectLevel` to keep track.
- bool matchInitializerList(const ValueDecl *Base, const Expr *InitExpr,
- unsigned ArrayElementIndirectLevel = 0) {
+ llvm::Error matchInitializerList(const ValueDecl *Base, const Expr *InitExpr,
+ unsigned ArrayElementIndirectLevel = 0) {
const InitListExpr *ILE = dyn_cast<InitListExpr>(InitExpr);
if (!ILE) {
if (!hasPtrOrArrType(InitExpr))
- return false;
+ return llvm::Error::success();
auto Expected = toEPL(Base);
- if (!Expected) {
- addError(Expected.takeError());
- return false;
- }
+ if (!Expected)
+ return Expected.takeError();
auto R = llvm::map_range(*Expected, [&ArrayElementIndirectLevel](
const EntityPointerLevel &EPL) {
@@ -148,23 +134,22 @@ class PointerAssignmentMatcher {
}
// Helper function for matchInitializerList that handles record:
- bool matchInitializerListForRecordDecl(const RecordDecl *RecordTy,
- const InitListExpr *ILE) {
+ llvm::Error matchInitializerListForRecordDecl(const RecordDecl *RecordTy,
+ const InitListExpr *ILE) {
if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RecordTy))
if (CXXRD->getNumBases() != 0) {
// FIXME: support this:
- addError(makeErrAtNode(
+ return makeErrAtNode(
Ctx, ILE,
"attempt to create pointer assignment edges between "
- "CXXRecordDecls with base classes and initializer-lists"));
- return false;
+ "CXXRecordDecls with base classes and initializer-lists");
}
// Handle union:
if (RecordTy->isUnion()) {
auto *InitField = ILE->getInitializedFieldInUnion();
if (!InitField)
- return false;
+ return llvm::Error::success();
assert(!ILE->inits().empty());
return matchInitializerList(InitField, ILE->getInit(0));
}
@@ -172,35 +157,30 @@ class PointerAssignmentMatcher {
ILE = ILE->getSemanticForm() ? ILE->getSemanticForm() : ILE;
auto FieldIter = RecordTy->field_begin();
- bool Matched = false;
assert(RecordTy->getNumFields() >= ILE->getNumInits());
- for (auto *Init : ILE->inits()) {
- if (matchInitializerList(*(FieldIter++), Init))
- Matched = true;
- }
- return Matched;
+ for (auto *Init : ILE->inits())
+ if (auto Err = matchInitializerList(*(FieldIter++), Init))
+ return Err;
+ return llvm::Error::success();
}
// Helper function for matchInitializerList that handles array:
- bool matchInitializerListForArray(const ValueDecl *Array,
- const InitListExpr *ILE,
- unsigned ArrayIndirectLevel = 0) {
- bool Matched = false;
-
+ llvm::Error matchInitializerListForArray(const ValueDecl *Array,
+ const InitListExpr *ILE,
+ unsigned ArrayIndirectLevel = 0) {
for (auto *E : ILE->inits())
- if (matchInitializerList(Array, E, ArrayIndirectLevel + 1))
- Matched = true;
- return Matched;
+ if (auto Err = matchInitializerList(Array, E, ArrayIndirectLevel + 1))
+ return Err;
+ return llvm::Error::success();
}
public:
- llvm::Error Error;
EdgeSet Results;
PointerAssignmentMatcher(
ASTContext &Ctx, std::function<EntityId(const EntityName &)> AddEntity)
- : Ctx(Ctx), AddEntity(AddEntity), Error(llvm::Error::success()) {}
+ : Ctx(Ctx), AddEntity(AddEntity) {}
// Match and extract assignments.
// The extraction function 'XF' can be described by the following rules:
@@ -215,7 +195,7 @@ class PointerAssignmentMatcher {
// ctor's body will be visited later.
// XF(T var = e) => XF(Var = e)
// XF(T var = init-list) => see `matchInitializerList`
- bool matches(const DynTypedNode &DynNode, const NamedDecl *RootDecl) {
+ llvm::Error matches(const DynTypedNode &DynNode, const NamedDecl *RootDecl) {
if (const Stmt *S = DynNode.get<Stmt>()) {
// Match 'p = q' whenever it has pointer or array type:
if (const auto *BO = dyn_cast<BinaryOperator>(S);
@@ -228,7 +208,7 @@ class PointerAssignmentMatcher {
const FunctionDecl *FD = CE->getDirectCallee();
if (!FD)
- return false;
+ return llvm::Error::success();
unsigned ArgIdx = 0;
@@ -247,7 +227,7 @@ class PointerAssignmentMatcher {
if (const auto *RS = dyn_cast<ReturnStmt>(S)) {
const Expr *RetExpr = RS->getRetValue();
if (!hasPtrOrArrType(RetExpr))
- return false;
+ return llvm::Error::success();
return addEdges(toEPL(RootDecl, true), RetExpr, false);
}
}
@@ -272,20 +252,18 @@ class PointerAssignmentMatcher {
// Match C++ constructor member-initializers:
if (const auto *CtorD = dyn_cast<CXXConstructorDecl>(D)) {
- bool Matched = false;
-
for (auto *E : CtorD->inits()) {
if (E->isDelegatingInitializer())
return matches(DynTypedNode::create(*E->getInit()), RootDecl);
if (const FieldDecl *FD = E->getMember(); FD && hasPtrOrArrType(FD)) {
- addEdges(E->getMember(), E->getInit());
- Matched = true;
+ if (auto Err = addEdges(E->getMember(), E->getInit()))
+ return Err;
}
}
- return Matched;
+ return llvm::Error::success();
}
}
- return false;
+ return llvm::Error::success();
}
};
} // namespace
@@ -305,12 +283,13 @@ class PointerFlowTUSummaryExtractor : public TUSummaryExtractor {
PointerAssignmentMatcher Matcher(
Ctx, [this](const EntityName &EN) { return addEntity(EN); });
auto MatchAction = [&Matcher, &Contributor](const DynTypedNode &Node) {
- Matcher.matches(Node, Contributor);
+ auto Err = Matcher.matches(Node, Contributor);
+
+ if (Err)
+ llvm::report_fatal_error(std::move(Err));
};
findMatchesIn(Contributor, MatchAction);
- if (Matcher.Error)
- return std::move(Matcher.Error);
return std::make_unique<PointerFlowEntitySummary>(
PointerFlowEntitySummary(std::move(Matcher.Results)));
}
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.cpp
index b5acbc7f82f63..774e7e94ac67a 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.cpp
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.cpp
@@ -36,7 +36,7 @@ class ContributorFinder : public DynamicRecursiveASTVisitor {
}
bool VisitLambdaExpr(LambdaExpr *L) override {
- // TraversetLambdaExpr directly visits the body stmt, but we need to collect
+ // TraverseLambdaExpr directly visits the body stmt, but we need to collect
// the CXXMethodDecl as a contributor:
VisitFunctionDecl(L->getCallOperator());
return true;
diff --git a/clang/test/Analysis/Scalable/PointerFlow/tu-summary-serialization.test b/clang/test/Analysis/Scalable/PointerFlow/tu-summary-serialization.test
index 4754e61637f34..c36e740350d45 100644
--- a/clang/test/Analysis/Scalable/PointerFlow/tu-summary-serialization.test
+++ b/clang/test/Analysis/Scalable/PointerFlow/tu-summary-serialization.test
@@ -13,7 +13,7 @@
// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-summary.json 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-BAD-SUMMARY
-// CHECK-BAD-SUMMARY: saw {} but expected a JSON array of arary of EntityPointerLevels
+// CHECK-BAD-SUMMARY: saw {} but expected a JSON array of array of EntityPointerLevels
// RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-array.json 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-BAD-ARRAY
>From b644c8d66f1b4143d2ceaaaeeb18f80d252080fe Mon Sep 17 00:00:00 2001
From: Ziqing Luo <ziqing_luo at apple.com>
Date: Thu, 16 Apr 2026 13:51:59 -0700
Subject: [PATCH 51/51] fix linux build fail
---
.../Analyses/SSAFAnalysesCommon.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
index e6759c1fb6e39..d2854f7fbae45 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
+++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h
@@ -12,6 +12,7 @@
#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H
+#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/Decl.h"
#include "llvm/Support/JSON.h"
More information about the llvm-branch-commits
mailing list