[clang] [clang][ssaf] Add ssaf-format to validate and convert summaries (PR #185575)
Aviral Goel via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 10 06:57:20 PDT 2026
https://github.com/aviralg updated https://github.com/llvm/llvm-project/pull/185575
>From 8c8b55b1624250ef0dfc74ffb526e0519d7e063a Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Mon, 9 Mar 2026 22:22:20 -0700
Subject: [PATCH 1/3] Add ssaf-format
---
.../Scalable/Serialization/JSONFormat.h | 4 +
.../Serialization/SerializationFormat.h | 7 +
.../JSONFormat/JSONFormatImpl.cpp | 7 +
.../Analysis/Scalable/ssaf-format/list.test | 9 +
clang/test/CMakeLists.txt | 1 +
clang/test/lit.cfg.py | 1 +
clang/tools/CMakeLists.txt | 1 +
clang/tools/ssaf-format/CMakeLists.txt | 14 +
clang/tools/ssaf-format/SSAFFormat.cpp | 483 ++++++++++++++++++
.../Registries/MockSerializationFormat.cpp | 7 +
.../Registries/MockSerializationFormat.h | 4 +
11 files changed, 538 insertions(+)
create mode 100644 clang/test/Analysis/Scalable/ssaf-format/list.test
create mode 100644 clang/tools/ssaf-format/CMakeLists.txt
create mode 100644 clang/tools/ssaf-format/SSAFFormat.cpp
diff --git a/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h b/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h
index 8c7cbd39e7099..aa979b03bbd15 100644
--- a/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h
+++ b/clang/include/clang/Analysis/Scalable/Serialization/JSONFormat.h
@@ -62,6 +62,10 @@ class JSONFormat final : public SerializationFormat {
llvm::Error writeLUSummaryEncoding(const LUSummaryEncoding &SummaryEncoding,
llvm::StringRef Path) override;
+ void forEachRegisteredAnalysis(
+ llvm::function_ref<void(llvm::StringRef Name, llvm::StringRef Desc)>
+ Callback) const override;
+
using EntityIdToJSONFn = llvm::function_ref<Object(EntityId)>;
using EntityIdFromJSONFn =
llvm::function_ref<llvm::Expected<EntityId>(const Object &)>;
diff --git a/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h b/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h
index 55e1ec7addc6c..cb545ca534ece 100644
--- a/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h
+++ b/clang/include/clang/Analysis/Scalable/Serialization/SerializationFormat.h
@@ -20,6 +20,7 @@
#include "clang/Analysis/Scalable/Model/BuildNamespace.h"
#include "clang/Analysis/Scalable/Model/SummaryName.h"
#include "clang/Analysis/Scalable/TUSummary/TUSummary.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
@@ -54,6 +55,12 @@ class SerializationFormat {
writeLUSummaryEncoding(const LUSummaryEncoding &SummaryEncoding,
llvm::StringRef Path) = 0;
+ /// Invokes \p Callback once for each analysis that has registered
+ /// serialization support for this format.
+ virtual void forEachRegisteredAnalysis(
+ llvm::function_ref<void(llvm::StringRef Name, llvm::StringRef Desc)>
+ Callback) const = 0;
+
protected:
// Helpers providing access to implementation details of basic data structures
// for efficient serialization/deserialization.
diff --git a/clang/lib/Analysis/Scalable/Serialization/JSONFormat/JSONFormatImpl.cpp b/clang/lib/Analysis/Scalable/Serialization/JSONFormat/JSONFormatImpl.cpp
index fa7cd8b7a346a..5cca372ee3b74 100644
--- a/clang/lib/Analysis/Scalable/Serialization/JSONFormat/JSONFormatImpl.cpp
+++ b/clang/lib/Analysis/Scalable/Serialization/JSONFormat/JSONFormatImpl.cpp
@@ -126,6 +126,13 @@ std::map<SummaryName, JSONFormat::FormatInfo> JSONFormat::initFormatInfos() {
return FormatInfos;
}
+void JSONFormat::forEachRegisteredAnalysis(
+ llvm::function_ref<void(llvm::StringRef, llvm::StringRef)> Callback)
+ const {
+ for (const auto &Entry : llvm::Registry<FormatInfo>::entries())
+ Callback(Entry.getName(), Entry.getDesc());
+}
+
//----------------------------------------------------------------------------
// SummaryName
//----------------------------------------------------------------------------
diff --git a/clang/test/Analysis/Scalable/ssaf-format/list.test b/clang/test/Analysis/Scalable/ssaf-format/list.test
new file mode 100644
index 0000000000000..b4bedcd99f2bc
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/list.test
@@ -0,0 +1,9 @@
+// Test ssaf-format --list output without any loaded plugins.
+
+// RUN: ssaf-format --list \
+// RUN: | FileCheck %s
+
+// CHECK: Registered serialization formats:
+// CHECK-EMPTY:
+// CHECK-NEXT: 1. JSON JSON serialization format
+// CHECK-NEXT: Analyses: (none)
diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt
index 24136256883f3..050a3fa5db5f7 100644
--- a/clang/test/CMakeLists.txt
+++ b/clang/test/CMakeLists.txt
@@ -107,6 +107,7 @@ list(APPEND CLANG_TEST_DEPS
diagtool
hmaptool
ssaf-linker
+ ssaf-format
)
if(CLANG_ENABLE_CIR)
diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py
index bff9480509231..512afed06a973 100644
--- a/clang/test/lit.cfg.py
+++ b/clang/test/lit.cfg.py
@@ -143,6 +143,7 @@
unresolved="ignore",
),
"ssaf-linker",
+ "ssaf-format",
]
if config.clang_examples:
diff --git a/clang/tools/CMakeLists.txt b/clang/tools/CMakeLists.txt
index aa4c4c226db57..6523471776cf1 100644
--- a/clang/tools/CMakeLists.txt
+++ b/clang/tools/CMakeLists.txt
@@ -53,4 +53,5 @@ add_llvm_external_project(clang-tools-extra extra)
add_clang_subdirectory(libclang)
add_clang_subdirectory(offload-arch)
+add_clang_subdirectory(ssaf-format)
add_clang_subdirectory(ssaf-linker)
diff --git a/clang/tools/ssaf-format/CMakeLists.txt b/clang/tools/ssaf-format/CMakeLists.txt
new file mode 100644
index 0000000000000..e517c5af920ec
--- /dev/null
+++ b/clang/tools/ssaf-format/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(LLVM_LINK_COMPONENTS
+ Option
+ Support
+ )
+
+add_clang_tool(ssaf-format
+ SSAFFormat.cpp
+ )
+
+clang_target_link_libraries(ssaf-format
+ PRIVATE
+ clangAnalysisScalable
+ clangBasic
+ )
diff --git a/clang/tools/ssaf-format/SSAFFormat.cpp b/clang/tools/ssaf-format/SSAFFormat.cpp
new file mode 100644
index 0000000000000..711813053bdbb
--- /dev/null
+++ b/clang/tools/ssaf-format/SSAFFormat.cpp
@@ -0,0 +1,483 @@
+//===- SSAFFormat.cpp - SSAF Format Tool ----------------------------------===//
+//
+// 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 implements the SSAF format tool that validates and converts
+// TU and LU summaries between registered serialization formats.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Scalable/EntityLinker/LUSummaryEncoding.h"
+#include "clang/Analysis/Scalable/EntityLinker/TUSummaryEncoding.h"
+#include "clang/Analysis/Scalable/Serialization/JSONFormat.h"
+#include "clang/Analysis/Scalable/Serialization/SerializationFormatRegistry.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
+#include <optional>
+#include <string>
+#include <system_error>
+
+using namespace llvm;
+using namespace clang::ssaf;
+
+namespace {
+
+namespace fs = llvm::sys::fs;
+namespace path = llvm::sys::path;
+
+//===----------------------------------------------------------------------===//
+// Summary Type
+//===----------------------------------------------------------------------===//
+
+enum class SummaryType { TU, LU };
+
+//===----------------------------------------------------------------------===//
+// Command-Line Options
+//===----------------------------------------------------------------------===//
+
+cl::OptionCategory SsafFormatCategory("ssaf-format options");
+
+cl::list<std::string> LoadPlugins("load",
+ cl::desc("Load a plugin shared library"),
+ cl::value_desc("path"),
+ cl::cat(SsafFormatCategory));
+
+// --type and the input file are required for convert/validateInput operations
+// but must be optional at the cl layer so that --list can be used standalone.
+cl::opt<SummaryType> Type(
+ "type", cl::desc("Summary type (required unless --list is given)"),
+ cl::values(clEnumValN(SummaryType::TU, "tu", "Translation unit summary"),
+ clEnumValN(SummaryType::LU, "lu", "Link unit summary")),
+ cl::cat(SsafFormatCategory));
+
+cl::opt<std::string> InputPath(cl::Positional, cl::desc("<input file>"),
+ cl::cat(SsafFormatCategory));
+
+cl::opt<std::string> OutputPath("o", cl::desc("Output summary path"),
+ cl::value_desc("path"),
+ cl::cat(SsafFormatCategory));
+
+cl::opt<bool> UseEncoding("encoding",
+ cl::desc("Read and write summary encodings rather "
+ "than decoded summaries"),
+ cl::cat(SsafFormatCategory));
+
+cl::opt<bool> ListFormats("list",
+ cl::desc("List registered serialization formats and "
+ "analyses, then exit"),
+ cl::init(false), cl::cat(SsafFormatCategory));
+
+llvm::StringRef ToolName;
+
+void printVersion(llvm::raw_ostream &OS) { OS << ToolName << " 0.1\n"; }
+
+//===----------------------------------------------------------------------===//
+// Error Messages
+//===----------------------------------------------------------------------===//
+
+namespace ErrorMessages {
+
+constexpr const char *FailedToLoadPlugin = "failed to load plugin '{0}': {1}";
+
+constexpr const char *CannotValidateSummary =
+ "failed to validate summary '{0}': {1}";
+
+constexpr const char *ExtensionNotSupplied = "Extension not supplied";
+
+constexpr const char *NoFormatForExtension =
+ "Format not registered for extension '{0}'";
+
+constexpr const char *OutputDirectoryMissing =
+ "Parent directory does not exist";
+
+constexpr const char *OutputFileAlreadyExists = "Output file already exists";
+
+constexpr const char *InputOutputSamePath =
+ "Input and Output resolve to the same path";
+
+} // namespace ErrorMessages
+
+//===----------------------------------------------------------------------===//
+// Diagnostic Utilities
+//===----------------------------------------------------------------------===//
+
+[[noreturn]] void fail(const char *Msg) {
+ llvm::WithColor::error(llvm::errs(), ToolName) << Msg << "\n";
+ llvm::sys::Process::Exit(1);
+}
+
+template <typename... Ts>
+[[noreturn]] void fail(const char *Fmt, Ts &&...Args) {
+ std::string Message = llvm::formatv(Fmt, std::forward<Ts>(Args)...);
+ fail(Message.data());
+}
+
+[[noreturn]] void fail(llvm::Error Err) {
+ fail(toString(std::move(Err)).data());
+}
+
+//===----------------------------------------------------------------------===//
+// Format Registry
+//===----------------------------------------------------------------------===//
+
+// FIXME: This will be revisited after we add support for registering formats
+// with extensions.
+SerializationFormat *getFormatForExtension(llvm::StringRef Extension) {
+ static llvm::SmallVector<
+ std::pair<std::string, std::unique_ptr<SerializationFormat>>, 4>
+ ExtensionFormatList;
+
+ // Most recently used format is most likely to be reused again.
+ auto ReversedList = llvm::reverse(ExtensionFormatList);
+ auto It = llvm::find_if(ReversedList, [&](const auto &Entry) {
+ return Entry.first == Extension;
+ });
+ if (It != ReversedList.end()) {
+ return It->second.get();
+ }
+
+ // SerializationFormats are uppercase while file extensions are lowercase.
+ std::string CapitalizedExtension = Extension.upper();
+
+ if (!isFormatRegistered(CapitalizedExtension)) {
+ return nullptr;
+ }
+
+ auto Format = makeFormat(CapitalizedExtension);
+ SerializationFormat *Result = Format.get();
+ assert(Result);
+
+ ExtensionFormatList.emplace_back(Extension, std::move(Format));
+
+ return Result;
+}
+
+//===----------------------------------------------------------------------===//
+// Format Listing
+//===----------------------------------------------------------------------===//
+
+constexpr size_t FormatIndent = 4;
+constexpr size_t AnalysisIndent = 4;
+
+struct AnalysisData {
+ std::string Name;
+ std::string Desc;
+};
+
+struct FormatData {
+ std::string Name;
+ std::string Desc;
+ llvm::SmallVector<AnalysisData> Analyses;
+};
+
+struct PrintLayout {
+ size_t FormatNumWidth;
+ size_t MaxFormatNameWidth;
+ size_t FormatNameCol;
+ size_t AnalysisCol;
+ size_t AnalysisNumWidth;
+ size_t MaxAnalysisNameWidth;
+};
+
+llvm::SmallVector<FormatData> collectFormats() {
+ llvm::SmallVector<FormatData> Formats;
+ for (const auto &Entry : SerializationFormatRegistry::entries()) {
+ FormatData FD;
+ FD.Name = Entry.getName().str();
+ FD.Desc = Entry.getDesc().str();
+ auto Format = Entry.instantiate();
+ Format->forEachRegisteredAnalysis(
+ [&](llvm::StringRef Name, llvm::StringRef Desc) {
+ FD.Analyses.push_back({Name.str(), Desc.str()});
+ });
+ Formats.push_back(std::move(FD));
+ }
+ return Formats;
+}
+
+void printAnalysis(const AnalysisData &AD, size_t AnalysisIndex,
+ size_t FormatIndex, const PrintLayout &Layout) {
+ std::string AnalysisNum = std::to_string(FormatIndex + 1) + "." +
+ std::to_string(AnalysisIndex + 1) + ".";
+ llvm::outs().indent(Layout.AnalysisCol)
+ << llvm::right_justify(AnalysisNum, Layout.AnalysisNumWidth) << " "
+ << llvm::left_justify(AD.Name, Layout.MaxAnalysisNameWidth) << " "
+ << AD.Desc << "\n";
+}
+
+void printAnalyses(const llvm::SmallVector<AnalysisData> &Analyses,
+ size_t FormatIndex, const PrintLayout &Layout) {
+ if (Analyses.empty()) {
+ llvm::outs().indent(Layout.FormatNameCol) << "Analyses: (none)\n";
+ return;
+ }
+
+ llvm::outs().indent(Layout.FormatNameCol) << "Analyses:\n";
+
+ for (size_t AnalysisIndex = 0; AnalysisIndex < Analyses.size();
+ ++AnalysisIndex) {
+ printAnalysis(Analyses[AnalysisIndex], AnalysisIndex, FormatIndex, Layout);
+ }
+}
+
+void printFormat(const FormatData &FD, size_t FormatIndex,
+ const PrintLayout &Layout) {
+ // Blank line before each format entry for readability.
+ llvm::outs() << "\n";
+
+ std::string FormatNum = std::to_string(FormatIndex + 1) + ".";
+ llvm::outs().indent(FormatIndent)
+ << llvm::right_justify(FormatNum, Layout.FormatNumWidth) << " "
+ << llvm::left_justify(FD.Name, Layout.MaxFormatNameWidth) << " "
+ << FD.Desc << "\n";
+
+ printAnalyses(FD.Analyses, FormatIndex, Layout);
+}
+
+void printFormats(const llvm::SmallVector<FormatData> &Formats,
+ const PrintLayout &Layout) {
+ llvm::outs() << "Registered serialization formats:\n";
+ for (size_t FormatIndex = 0; FormatIndex < Formats.size(); ++FormatIndex) {
+ printFormat(Formats[FormatIndex], FormatIndex, Layout);
+ }
+}
+
+PrintLayout computePrintLayout(const llvm::SmallVector<FormatData> &Formats) {
+ size_t MaxFormatNameWidth = 0;
+ size_t MaxAnalysisCount = 0;
+ size_t MaxAnalysisNameWidth = 0;
+ for (const auto &FD : Formats) {
+ MaxFormatNameWidth = std::max(MaxFormatNameWidth, FD.Name.size());
+ MaxAnalysisCount = std::max(MaxAnalysisCount, FD.Analyses.size());
+ for (const auto &AD : FD.Analyses) {
+ MaxAnalysisNameWidth = std::max(MaxAnalysisNameWidth, AD.Name.size());
+ }
+ }
+
+ // Width of the widest format number string, e.g. "10." -> 3.
+ size_t FormatNumWidth =
+ std::to_string(Formats.size()).size() + 1; // +1 for '.'
+ // Width of the widest analysis number string, e.g. "10.10." -> 6.
+ size_t AnalysisNumWidth = std::to_string(Formats.size()).size() + 1 +
+ std::to_string(MaxAnalysisCount).size() + 1;
+
+ // Where the format name starts (also where "Analyses:" is indented to).
+ size_t FormatNameCol = FormatIndent + FormatNumWidth + 1;
+ // Where the analysis number starts.
+ size_t AnalysisCol = FormatNameCol + AnalysisIndent;
+
+ return {
+ FormatNumWidth, MaxFormatNameWidth, FormatNameCol,
+ AnalysisCol, AnalysisNumWidth, MaxAnalysisNameWidth,
+ };
+}
+
+void listFormats() {
+ llvm::SmallVector<FormatData> Formats = collectFormats();
+ if (Formats.empty()) {
+ llvm::outs() << "No serialization formats registered.\n";
+ return;
+ }
+ printFormats(Formats, computePrintLayout(Formats));
+}
+
+//===----------------------------------------------------------------------===//
+// Plugin Loading
+//===----------------------------------------------------------------------===//
+
+void loadPlugins() {
+ for (const auto &PluginPath : LoadPlugins) {
+ std::string ErrMsg;
+ if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(PluginPath.c_str(),
+ &ErrMsg)) {
+ fail(ErrorMessages::FailedToLoadPlugin, PluginPath, ErrMsg);
+ }
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Input Validation
+//===----------------------------------------------------------------------===//
+
+struct SummaryFile {
+ std::string Path;
+ SerializationFormat *Format = nullptr;
+
+ static SummaryFile fromPath(llvm::StringRef Path) {
+ llvm::StringRef Extension = path::extension(Path);
+ if (Extension.empty()) {
+ fail(ErrorMessages::CannotValidateSummary, Path,
+ ErrorMessages::ExtensionNotSupplied);
+ }
+
+ Extension = Extension.drop_front();
+ SerializationFormat *Format = getFormatForExtension(Extension);
+ if (!Format) {
+ std::string Msg =
+ llvm::formatv(ErrorMessages::NoFormatForExtension, Extension);
+ fail(ErrorMessages::CannotValidateSummary, Path, Msg);
+ }
+
+ return {Path.str(), Format};
+ }
+};
+
+struct FormatInput {
+ SummaryFile InputFile;
+ std::optional<SummaryFile> OutputFile;
+};
+
+FormatInput validateInput() {
+ assert(!ListFormats);
+
+ FormatInput FI;
+
+ // Validate Type explicitly since we don't want to specify it if --list is
+ // provided.
+ if (!Type.getNumOccurrences()) {
+ fail("'--type' option is required");
+ }
+
+ // Validate the input path.
+ {
+ if (InputPath.empty()) {
+ fail("no input file specified");
+ }
+
+ llvm::SmallString<256> RealInputPath;
+ std::error_code EC =
+ fs::real_path(InputPath, RealInputPath, /*expand_tilde=*/true);
+ if (EC) {
+ fail(ErrorMessages::CannotValidateSummary, InputPath, EC.message());
+ }
+
+ FI.InputFile = SummaryFile::fromPath(RealInputPath);
+ }
+
+ // Validate the output path.
+ if (!OutputPath.empty()) {
+ llvm::StringRef ParentDir = path::parent_path(OutputPath);
+ llvm::StringRef DirToCheck = ParentDir.empty() ? "." : ParentDir;
+
+ if (!fs::exists(DirToCheck)) {
+ fail(ErrorMessages::CannotValidateSummary, OutputPath,
+ ErrorMessages::OutputDirectoryMissing);
+ }
+
+ // Reconstruct the real output path from the real parent directory and the
+ // output filename. The output file does not exist yet so real_path cannot
+ // be called on the full output path directly.
+ llvm::SmallString<256> RealParentDir;
+ if (std::error_code EC = fs::real_path(DirToCheck, RealParentDir)) {
+ fail(ErrorMessages::CannotValidateSummary, OutputPath, EC.message());
+ }
+
+ llvm::SmallString<256> RealOutputPath = RealParentDir;
+ path::append(RealOutputPath, path::filename(OutputPath));
+
+ if (RealOutputPath == FI.InputFile.Path) {
+ fail(ErrorMessages::CannotValidateSummary, OutputPath,
+ ErrorMessages::InputOutputSamePath);
+ }
+
+ if (fs::exists(RealOutputPath)) {
+ fail(ErrorMessages::CannotValidateSummary, OutputPath,
+ ErrorMessages::OutputFileAlreadyExists);
+ }
+
+ FI.OutputFile = SummaryFile::fromPath(RealOutputPath);
+ }
+ return FI;
+}
+
+//===----------------------------------------------------------------------===//
+// Format Conversion
+//===----------------------------------------------------------------------===//
+
+template <typename ReadFn, typename WriteFn>
+void run(const FormatInput &FI, ReadFn Read, WriteFn Write) {
+ auto ExpectedResult = (FI.InputFile.Format->*Read)(FI.InputFile.Path);
+ if (!ExpectedResult) {
+ fail(ExpectedResult.takeError());
+ }
+
+ if (!FI.OutputFile) {
+ return;
+ }
+
+ auto Err =
+ (FI.OutputFile->Format->*Write)(*ExpectedResult, FI.OutputFile->Path);
+ if (Err) {
+ fail(std::move(Err));
+ }
+}
+
+void convert(const FormatInput &FI) {
+ switch (Type) {
+ case SummaryType::TU:
+ if (UseEncoding) {
+ run(FI, &SerializationFormat::readTUSummaryEncoding,
+ &SerializationFormat::writeTUSummaryEncoding);
+ } else {
+ run(FI, &SerializationFormat::readTUSummary,
+ &SerializationFormat::writeTUSummary);
+ }
+ return;
+ case SummaryType::LU:
+ if (UseEncoding) {
+ run(FI, &SerializationFormat::readLUSummaryEncoding,
+ &SerializationFormat::writeLUSummaryEncoding);
+ } else {
+ run(FI, &SerializationFormat::readLUSummary,
+ &SerializationFormat::writeLUSummary);
+ }
+ return;
+ }
+
+ llvm_unreachable("Unhandled SummaryType variant");
+}
+
+} // namespace
+
+//===----------------------------------------------------------------------===//
+// Driver
+//===----------------------------------------------------------------------===//
+
+int main(int argc, const char **argv) {
+ InitLLVM X(argc, argv);
+ // path::stem strips the .exe extension on Windows so ToolName is consistent.
+ ToolName = path::stem(argv[0]);
+
+ cl::HideUnrelatedOptions(SsafFormatCategory);
+ cl::SetVersionPrinter(printVersion);
+ cl::ParseCommandLineOptions(argc, argv, "SSAF Format\n");
+
+ loadPlugins();
+
+ initializeJSONFormat();
+
+ if (ListFormats) {
+ listFormats();
+ } else {
+ FormatInput FI = validateInput();
+ convert(FI);
+ }
+
+ return 0;
+}
diff --git a/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.cpp b/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.cpp
index 13c8e103768a1..3f074076b793f 100644
--- a/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.cpp
+++ b/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.cpp
@@ -44,6 +44,13 @@ MockSerializationFormat::MockSerializationFormat() {
}
}
+void MockSerializationFormat::forEachRegisteredAnalysis(
+ llvm::function_ref<void(llvm::StringRef, llvm::StringRef)> Callback)
+ const {
+ for (const auto &Entry : llvm::Registry<FormatInfo>::entries())
+ Callback(Entry.getName(), Entry.getDesc());
+}
+
llvm::Expected<TUSummary>
MockSerializationFormat::readTUSummary(llvm::StringRef Path) {
BuildNamespace NS(BuildNamespaceKind::CompilationUnit, "Mock.cpp");
diff --git a/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.h b/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.h
index 06495f4a83929..d15ff27d2b26f 100644
--- a/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.h
+++ b/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.h
@@ -44,6 +44,10 @@ class MockSerializationFormat final : public SerializationFormat {
llvm::Error writeLUSummaryEncoding(const LUSummaryEncoding &SummaryEncoding,
llvm::StringRef Path) override;
+ void forEachRegisteredAnalysis(
+ llvm::function_ref<void(llvm::StringRef Name, llvm::StringRef Desc)>
+ Callback) const override;
+
struct SpecialFileRepresentation {
std::string MockRepresentation;
};
>From 8c0ed7fa982386b24fa83ccc53f1940896363f9a Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Mon, 9 Mar 2026 22:36:42 -0700
Subject: [PATCH 2/3] Format
---
.../Scalable/Serialization/JSONFormat/JSONFormatImpl.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/Analysis/Scalable/Serialization/JSONFormat/JSONFormatImpl.cpp b/clang/lib/Analysis/Scalable/Serialization/JSONFormat/JSONFormatImpl.cpp
index 5cca372ee3b74..86f2aa58f766c 100644
--- a/clang/lib/Analysis/Scalable/Serialization/JSONFormat/JSONFormatImpl.cpp
+++ b/clang/lib/Analysis/Scalable/Serialization/JSONFormat/JSONFormatImpl.cpp
@@ -127,8 +127,7 @@ std::map<SummaryName, JSONFormat::FormatInfo> JSONFormat::initFormatInfos() {
}
void JSONFormat::forEachRegisteredAnalysis(
- llvm::function_ref<void(llvm::StringRef, llvm::StringRef)> Callback)
- const {
+ llvm::function_ref<void(llvm::StringRef, llvm::StringRef)> Callback) const {
for (const auto &Entry : llvm::Registry<FormatInfo>::entries())
Callback(Entry.getName(), Entry.getDesc());
}
>From 36f4f688621858cf2b34369cea8ac39a1a495946 Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Tue, 10 Mar 2026 06:56:30 -0700
Subject: [PATCH 3/3] Format
---
.../Analysis/Scalable/Registries/MockSerializationFormat.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.cpp b/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.cpp
index 3f074076b793f..2302a7e04fe89 100644
--- a/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.cpp
+++ b/clang/unittests/Analysis/Scalable/Registries/MockSerializationFormat.cpp
@@ -45,8 +45,7 @@ MockSerializationFormat::MockSerializationFormat() {
}
void MockSerializationFormat::forEachRegisteredAnalysis(
- llvm::function_ref<void(llvm::StringRef, llvm::StringRef)> Callback)
- const {
+ llvm::function_ref<void(llvm::StringRef, llvm::StringRef)> Callback) const {
for (const auto &Entry : llvm::Registry<FormatInfo>::entries())
Callback(Entry.getName(), Entry.getDesc());
}
More information about the cfe-commits
mailing list