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