[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
Wed Apr 8 16:31:29 PDT 2026
https://github.com/ziqingluo-90 updated https://github.com/llvm/llvm-project/pull/188654
>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 1/7] [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 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 2/7] 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 3/7] 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 4/7] 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 5/7] 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 6/7] 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 7/7] 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;
},
More information about the llvm-branch-commits
mailing list