[clang] [clang][ssaf] Add source-transformation library scaffolding (PR #204214)
Jan Korous via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 16 13:51:20 PDT 2026
https://github.com/jkorous-apple updated https://github.com/llvm/llvm-project/pull/204214
>From 577a6f69afd9d3aa4745a9a0974ef1b010bc8df5 Mon Sep 17 00:00:00 2001
From: Jan Korous <jkorous at apple.com>
Date: Fri, 12 Jun 2026 19:15:00 -0700
Subject: [PATCH] [clang][ssaf] Add source-transformation library scaffolding
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Introduces the abstract base classes, registries, and force-linker
anchor for the SSAF source-transformation library.
A `Transformation` is an `ASTConsumer` that consumes a previously
computed `WPASuite` and emits source edits and findings through two
sinks: `SourceEditEmitter` (accumulates `clang::tooling::Replacement`s)
and `TransformationReportEmitter` (accumulates `(ruleId, level, range,
message)` tuples). The accumulated state is then handed to a
`SourceEditFormat` and a `TransformationReportFormat` for serialization.
Three `llvm::Registry`-backed registries — keyed by transformation
name and by file extension respectively — let transformations and
formats be linked in statically (with a force-linker anchor) or loaded
dynamically as a clang plugin. Each registry exposes the standard
`is*Registered` / `make*` / `printAvailable*` helpers used elsewhere
in SSAF.
No transformation or format ships yet; this commit only adds the
plumbing. Existing tools (`clang-ssaf-format`, `clang-ssaf-linker`,
`clang-ssaf-analyzer`) and the clang driver gain the new lib in their
link line so the framework anchor resolves under static linking.
Assisted-By: Claude Opus 4.7
---
.../BuiltinAnchorSources.def | 1 +
.../SourceTransformation/SourceEditEmitter.h | 29 +++++++
.../SourceTransformation/Transformation.h | 38 +++++++++
.../TransformationRegistry.h | 66 +++++++++++++++
.../TransformationReportEmitter.h | 35 ++++++++
clang/lib/Driver/CMakeLists.txt | 1 +
clang/lib/FrontendTool/CMakeLists.txt | 1 +
.../CMakeLists.txt | 1 +
.../SourceTransformation/CMakeLists.txt | 13 +++
.../TransformationRegistry.cpp | 44 ++++++++++
.../tools/clang-ssaf-analyzer/CMakeLists.txt | 1 +
clang/tools/clang-ssaf-format/CMakeLists.txt | 1 +
clang/tools/clang-ssaf-linker/CMakeLists.txt | 1 +
.../CMakeLists.txt | 4 +
.../SourceTransformation/EmitterTest.cpp | 81 +++++++++++++++++++
.../SourceTransformation/RegistryTest.cpp | 78 ++++++++++++++++++
16 files changed, 395 insertions(+)
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/SourceTransformation/SourceEditEmitter.h
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/SourceTransformation/Transformation.h
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/SourceTransformation/TransformationRegistry.h
create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/SourceTransformation/TransformationReportEmitter.h
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/SourceTransformation/CMakeLists.txt
create mode 100644 clang/lib/ScalableStaticAnalysisFramework/SourceTransformation/TransformationRegistry.cpp
create mode 100644 clang/unittests/ScalableStaticAnalysisFramework/SourceTransformation/EmitterTest.cpp
create mode 100644 clang/unittests/ScalableStaticAnalysisFramework/SourceTransformation/RegistryTest.cpp
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/BuiltinAnchorSources.def b/clang/include/clang/ScalableStaticAnalysisFramework/BuiltinAnchorSources.def
index 7dd866047f556..ba4944ea984cb 100644
--- a/clang/include/clang/ScalableStaticAnalysisFramework/BuiltinAnchorSources.def
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/BuiltinAnchorSources.def
@@ -23,6 +23,7 @@ ANCHOR(JSONFormatAnchorSource)
ANCHOR(PointerFlowAnalysisAnchorSource)
ANCHOR(PointerFlowExtractorAnchorSource)
ANCHOR(PointerFlowJSONFormatAnchorSource)
+ANCHOR(SSAFSourceTransformationAnchorSource)
ANCHOR(UnsafeBufferUsageAnalysisAnchorSource)
ANCHOR(UnsafeBufferUsageExtractorAnchorSource)
ANCHOR(UnsafeBufferUsageJSONFormatAnchorSource)
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SourceTransformation/SourceEditEmitter.h b/clang/include/clang/ScalableStaticAnalysisFramework/SourceTransformation/SourceEditEmitter.h
new file mode 100644
index 0000000000000..e96c9846a35f2
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SourceTransformation/SourceEditEmitter.h
@@ -0,0 +1,29 @@
+//===- SourceEditEmitter.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Abstract accumulator for source edits.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SOURCETRANSFORMATION_SOURCEEDITEMITTER_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SOURCETRANSFORMATION_SOURCEEDITEMITTER_H
+
+#include "clang/Tooling/Core/Replacement.h"
+
+namespace clang::ssaf {
+
+class SourceEditEmitter {
+public:
+ virtual ~SourceEditEmitter() = default;
+
+ virtual void addReplacement(clang::tooling::Replacement R) = 0;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SOURCETRANSFORMATION_SOURCEEDITEMITTER_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SourceTransformation/Transformation.h b/clang/include/clang/ScalableStaticAnalysisFramework/SourceTransformation/Transformation.h
new file mode 100644
index 0000000000000..5553b7796b1ad
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SourceTransformation/Transformation.h
@@ -0,0 +1,38 @@
+//===- Transformation.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Abstract base class for source transformations. A Transformation is an
+// ASTConsumer that consumes a previously computed WPASuite.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SOURCETRANSFORMATION_TRANSFORMATION_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SOURCETRANSFORMATION_TRANSFORMATION_H
+
+#include "clang/AST/ASTConsumer.h"
+#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/WPASuite.h"
+#include "clang/ScalableStaticAnalysisFramework/SourceTransformation/SourceEditEmitter.h"
+#include "clang/ScalableStaticAnalysisFramework/SourceTransformation/TransformationReportEmitter.h"
+
+namespace clang::ssaf {
+
+class Transformation : public clang::ASTConsumer {
+public:
+ Transformation(const WPASuite &Suite, SourceEditEmitter &Edits,
+ TransformationReportEmitter &Report)
+ : Suite(Suite), Edits(Edits), Report(Report) {}
+
+protected:
+ const WPASuite &Suite;
+ SourceEditEmitter &Edits;
+ TransformationReportEmitter &Report;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SOURCETRANSFORMATION_TRANSFORMATION_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SourceTransformation/TransformationRegistry.h b/clang/include/clang/ScalableStaticAnalysisFramework/SourceTransformation/TransformationRegistry.h
new file mode 100644
index 0000000000000..231a810f60f26
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SourceTransformation/TransformationRegistry.h
@@ -0,0 +1,66 @@
+//===- TransformationRegistry.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Registry for Transformations, and some helper functions.
+// To register a transformation, insert this code:
+//
+// namespace clang::ssaf {
+// // NOLINTNEXTLINE(misc-use-internal-linkage)
+// volatile int MyTransformationAnchorSource = 0;
+// } // namespace clang::ssaf
+// static TransformationRegistry::Add<MyTransformation>
+// X("MyTransformation", "My awesome transformation");
+//
+// For a statically-linked transformation also extend the `AnchorSources`
+// list in
+// clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h
+// (plugin-loaded transformations do not need an anchor — the dynamic loader
+// runs every global ctor on load).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SOURCETRANSFORMATION_TRANSFORMATIONREGISTRY_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SOURCETRANSFORMATION_TRANSFORMATIONREGISTRY_H
+
+#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/WPASuite.h"
+#include "clang/ScalableStaticAnalysisFramework/SourceTransformation/SourceEditEmitter.h"
+#include "clang/ScalableStaticAnalysisFramework/SourceTransformation/Transformation.h"
+#include "clang/ScalableStaticAnalysisFramework/SourceTransformation/TransformationReportEmitter.h"
+#include "clang/Support/Compiler.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Registry.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
+
+namespace clang::ssaf {
+
+/// Check if a Transformation was registered with a given name.
+bool isTransformationRegistered(llvm::StringRef Name);
+
+/// Try to instantiate a Transformation with a given name.
+/// This might return null if the construction of the desired Transformation
+/// failed.
+/// It's a fatal error if there is no transformation registered with the name.
+std::unique_ptr<Transformation>
+makeTransformation(llvm::StringRef Name, const WPASuite &Suite,
+ SourceEditEmitter &Edits,
+ TransformationReportEmitter &Report);
+
+/// Print the list of available Transformations.
+void printAvailableTransformations(llvm::raw_ostream &OS);
+
+// Registry for adding new Transformation implementations.
+using TransformationRegistry =
+ llvm::Registry<Transformation, const WPASuite &, SourceEditEmitter &,
+ TransformationReportEmitter &>;
+
+} // namespace clang::ssaf
+
+LLVM_DECLARE_REGISTRY(clang::ssaf::TransformationRegistry)
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SOURCETRANSFORMATION_TRANSFORMATIONREGISTRY_H
diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SourceTransformation/TransformationReportEmitter.h b/clang/include/clang/ScalableStaticAnalysisFramework/SourceTransformation/TransformationReportEmitter.h
new file mode 100644
index 0000000000000..c48a35009c034
--- /dev/null
+++ b/clang/include/clang/ScalableStaticAnalysisFramework/SourceTransformation/TransformationReportEmitter.h
@@ -0,0 +1,35 @@
+//===- TransformationReportEmitter.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Abstract accumulator for the transformation report.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SOURCETRANSFORMATION_TRANSFORMATIONREPORTEMITTER_H
+#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SOURCETRANSFORMATION_TRANSFORMATIONREPORTEMITTER_H
+
+#include "clang/Basic/Sarif.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang::ssaf {
+
+class TransformationReportEmitter {
+public:
+ virtual ~TransformationReportEmitter() = default;
+
+ /// An invalid \p Range signals "no location"; the format writer drops the
+ /// location object entirely rather than fabricating a placeholder.
+ virtual void addResult(llvm::StringRef RuleId, clang::SarifResultLevel Level,
+ clang::CharSourceRange Range,
+ llvm::StringRef Message) = 0;
+};
+
+} // namespace clang::ssaf
+
+#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SOURCETRANSFORMATION_TRANSFORMATIONREPORTEMITTER_H
diff --git a/clang/lib/Driver/CMakeLists.txt b/clang/lib/Driver/CMakeLists.txt
index 5fe10052d267f..cd410899c450f 100644
--- a/clang/lib/Driver/CMakeLists.txt
+++ b/clang/lib/Driver/CMakeLists.txt
@@ -115,6 +115,7 @@ add_clang_library(clangDriver
clangScalableStaticAnalysisFrameworkAnalyses
clangScalableStaticAnalysisFrameworkCore
clangScalableStaticAnalysisFrameworkFrontend
+ clangScalableStaticAnalysisFrameworkSourceTransformation
clangSerialization
clangLex
clangOptions
diff --git a/clang/lib/FrontendTool/CMakeLists.txt b/clang/lib/FrontendTool/CMakeLists.txt
index 24623303e6bdb..543aa06090ec8 100644
--- a/clang/lib/FrontendTool/CMakeLists.txt
+++ b/clang/lib/FrontendTool/CMakeLists.txt
@@ -7,6 +7,7 @@ set(link_libs
clangScalableStaticAnalysisFrameworkAnalyses
clangScalableStaticAnalysisFrameworkCore
clangScalableStaticAnalysisFrameworkFrontend
+ clangScalableStaticAnalysisFrameworkSourceTransformation
clangBasic
clangCodeGen
clangDriver
diff --git a/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
index e09c44b7cfd52..b214c06644db7 100644
--- a/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -2,4 +2,5 @@ add_subdirectory(Analyses)
add_subdirectory(Core)
add_subdirectory(Frontend)
add_subdirectory(Plugins)
+add_subdirectory(SourceTransformation)
add_subdirectory(Tool)
diff --git a/clang/lib/ScalableStaticAnalysisFramework/SourceTransformation/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/SourceTransformation/CMakeLists.txt
new file mode 100644
index 0000000000000..c96a386977487
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/SourceTransformation/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_library(clangScalableStaticAnalysisFrameworkSourceTransformation
+ TransformationRegistry.cpp
+
+ LINK_LIBS
+ clangAST
+ clangBasic
+ clangScalableStaticAnalysisFrameworkCore
+ clangToolingCore
+ )
diff --git a/clang/lib/ScalableStaticAnalysisFramework/SourceTransformation/TransformationRegistry.cpp b/clang/lib/ScalableStaticAnalysisFramework/SourceTransformation/TransformationRegistry.cpp
new file mode 100644
index 0000000000000..9d770c8bcd0f9
--- /dev/null
+++ b/clang/lib/ScalableStaticAnalysisFramework/SourceTransformation/TransformationRegistry.cpp
@@ -0,0 +1,44 @@
+//===- TransformationRegistry.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ScalableStaticAnalysisFramework/SourceTransformation/TransformationRegistry.h"
+#include <memory>
+
+using namespace clang;
+using namespace ssaf;
+
+namespace clang::ssaf {
+// NOLINTNEXTLINE(misc-use-internal-linkage)
+volatile int SSAFSourceTransformationAnchorSource = 0;
+} // namespace clang::ssaf
+
+LLVM_DEFINE_REGISTRY(clang::ssaf::TransformationRegistry)
+
+bool ssaf::isTransformationRegistered(llvm::StringRef Name) {
+ for (const auto &Entry : TransformationRegistry::entries())
+ if (Entry.getName() == Name)
+ return true;
+ return false;
+}
+
+std::unique_ptr<Transformation>
+ssaf::makeTransformation(llvm::StringRef Name, const WPASuite &Suite,
+ SourceEditEmitter &Edits,
+ TransformationReportEmitter &Report) {
+ for (const auto &Entry : TransformationRegistry::entries())
+ if (Entry.getName() == Name)
+ return Entry.instantiate(Suite, Edits, Report);
+ assert(false && "Unknown Transformation name");
+ return nullptr;
+}
+
+void ssaf::printAvailableTransformations(llvm::raw_ostream &OS) {
+ OS << "OVERVIEW: Available SSAF source transformations:\n\n";
+ for (const auto &Entry : TransformationRegistry::entries())
+ OS << " " << Entry.getName() << " - " << Entry.getDesc() << "\n";
+}
diff --git a/clang/tools/clang-ssaf-analyzer/CMakeLists.txt b/clang/tools/clang-ssaf-analyzer/CMakeLists.txt
index 67732867181b6..d58ec773ff04b 100644
--- a/clang/tools/clang-ssaf-analyzer/CMakeLists.txt
+++ b/clang/tools/clang-ssaf-analyzer/CMakeLists.txt
@@ -17,6 +17,7 @@ clang_target_link_libraries(clang-ssaf-analyzer
clangBasic
clangScalableStaticAnalysisFrameworkAnalyses
clangScalableStaticAnalysisFrameworkCore
+ clangScalableStaticAnalysisFrameworkSourceTransformation
clangScalableStaticAnalysisFrameworkTool
)
diff --git a/clang/tools/clang-ssaf-format/CMakeLists.txt b/clang/tools/clang-ssaf-format/CMakeLists.txt
index 33ce432be3f4a..5d7a6c70c73fb 100644
--- a/clang/tools/clang-ssaf-format/CMakeLists.txt
+++ b/clang/tools/clang-ssaf-format/CMakeLists.txt
@@ -17,6 +17,7 @@ clang_target_link_libraries(clang-ssaf-format
clangBasic
clangScalableStaticAnalysisFrameworkAnalyses
clangScalableStaticAnalysisFrameworkCore
+ clangScalableStaticAnalysisFrameworkSourceTransformation
clangScalableStaticAnalysisFrameworkTool
)
diff --git a/clang/tools/clang-ssaf-linker/CMakeLists.txt b/clang/tools/clang-ssaf-linker/CMakeLists.txt
index af65aaa3b1aeb..89d72a45a734e 100644
--- a/clang/tools/clang-ssaf-linker/CMakeLists.txt
+++ b/clang/tools/clang-ssaf-linker/CMakeLists.txt
@@ -12,5 +12,6 @@ clang_target_link_libraries(clang-ssaf-linker
clangBasic
clangScalableStaticAnalysisFrameworkAnalyses
clangScalableStaticAnalysisFrameworkCore
+ clangScalableStaticAnalysisFrameworkSourceTransformation
clangScalableStaticAnalysisFrameworkTool
)
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
index e852d99d34781..8090ea96cbd5c 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysisFramework/CMakeLists.txt
@@ -24,6 +24,8 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
Serialization/JSONFormatTest/JSONFormatTest.cpp
Serialization/JSONFormatTest/LUSummaryTest.cpp
Serialization/JSONFormatTest/TUSummaryTest.cpp
+ SourceTransformation/EmitterTest.cpp
+ SourceTransformation/RegistryTest.cpp
SummaryData/SummaryDataTest.cpp
SummaryNameTest.cpp
TestFixture.cpp
@@ -39,8 +41,10 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
clangScalableStaticAnalysisFrameworkAnalyses
clangScalableStaticAnalysisFrameworkCore
clangScalableStaticAnalysisFrameworkFrontend
+ clangScalableStaticAnalysisFrameworkSourceTransformation
clangSerialization
clangTooling
+ clangToolingCore
LINK_LIBS
LLVMTestingSupport
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/SourceTransformation/EmitterTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/SourceTransformation/EmitterTest.cpp
new file mode 100644
index 0000000000000..d730c6c337c45
--- /dev/null
+++ b/clang/unittests/ScalableStaticAnalysisFramework/SourceTransformation/EmitterTest.cpp
@@ -0,0 +1,81 @@
+//===- EmitterTest.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/Sarif.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/ScalableStaticAnalysisFramework/SourceTransformation/SourceEditEmitter.h"
+#include "clang/ScalableStaticAnalysisFramework/SourceTransformation/TransformationReportEmitter.h"
+#include "gtest/gtest.h"
+#include <vector>
+
+using namespace llvm;
+using namespace clang;
+using namespace ssaf;
+
+namespace {
+
+class RecordingEditEmitter : public SourceEditEmitter {
+public:
+ std::vector<clang::tooling::Replacement> Replacements;
+
+ void addReplacement(clang::tooling::Replacement R) override {
+ Replacements.push_back(std::move(R));
+ }
+};
+
+class RecordingReportEmitter : public TransformationReportEmitter {
+public:
+ struct Entry {
+ std::string RuleId;
+ clang::SarifResultLevel Level;
+ clang::CharSourceRange Range;
+ std::string Message;
+ };
+ std::vector<Entry> Results;
+
+ void addResult(StringRef RuleId, clang::SarifResultLevel Level,
+ clang::CharSourceRange Range, StringRef Message) override {
+ Results.push_back({RuleId.str(), Level, Range, Message.str()});
+ }
+};
+
+TEST(SourceEditEmitterTest, AccumulatesInOrder) {
+ RecordingEditEmitter E;
+ E.addReplacement(clang::tooling::Replacement("a.cpp", 0, 0, "// 1"));
+ E.addReplacement(clang::tooling::Replacement("a.cpp", 10, 0, "// 2"));
+ ASSERT_EQ(E.Replacements.size(), 2u);
+ EXPECT_EQ(E.Replacements[0].getReplacementText(), "// 1");
+ EXPECT_EQ(E.Replacements[1].getReplacementText(), "// 2");
+ EXPECT_EQ(E.Replacements[0].getOffset(), 0u);
+ EXPECT_EQ(E.Replacements[1].getOffset(), 10u);
+}
+
+TEST(TransformationReportEmitterTest, AccumulatesInOrder) {
+ RecordingReportEmitter R;
+ R.addResult("rule-a", clang::SarifResultLevel::Note, clang::CharSourceRange{},
+ "first");
+ R.addResult("rule-b", clang::SarifResultLevel::Warning,
+ clang::CharSourceRange{}, "second");
+ ASSERT_EQ(R.Results.size(), 2u);
+ EXPECT_EQ(R.Results[0].RuleId, "rule-a");
+ EXPECT_EQ(R.Results[0].Level, clang::SarifResultLevel::Note);
+ EXPECT_EQ(R.Results[0].Message, "first");
+ EXPECT_EQ(R.Results[1].RuleId, "rule-b");
+ EXPECT_EQ(R.Results[1].Level, clang::SarifResultLevel::Warning);
+ EXPECT_EQ(R.Results[1].Message, "second");
+}
+
+TEST(TransformationReportEmitterTest, AcceptsInvalidRange) {
+ RecordingReportEmitter R;
+ R.addResult("rule", clang::SarifResultLevel::Note, clang::CharSourceRange{},
+ "no-location");
+ ASSERT_EQ(R.Results.size(), 1u);
+ EXPECT_FALSE(R.Results[0].Range.isValid());
+}
+
+} // namespace
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/SourceTransformation/RegistryTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/SourceTransformation/RegistryTest.cpp
new file mode 100644
index 0000000000000..0c68cd95dd499
--- /dev/null
+++ b/clang/unittests/ScalableStaticAnalysisFramework/SourceTransformation/RegistryTest.cpp
@@ -0,0 +1,78 @@
+//===- RegistryTest.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestFixture.h"
+#include "clang/ScalableStaticAnalysisFramework/SourceTransformation/Transformation.h"
+#include "clang/ScalableStaticAnalysisFramework/SourceTransformation/TransformationRegistry.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+using namespace ssaf;
+
+namespace {
+
+class StubEditEmitter : public SourceEditEmitter {
+public:
+ void addReplacement(clang::tooling::Replacement) override {}
+};
+
+class StubReportEmitter : public TransformationReportEmitter {
+public:
+ void addResult(StringRef, clang::SarifResultLevel, clang::CharSourceRange,
+ StringRef) override {}
+};
+
+class StubTransformation : public Transformation {
+public:
+ using Transformation::Transformation;
+};
+
+} // namespace
+
+static TransformationRegistry::Add<StubTransformation>
+ RegisterStubTransformation("stub-transformation",
+ "A transformation for testing");
+
+namespace {
+
+class TransformationRegistryTest : public TestFixture {};
+
+TEST_F(TransformationRegistryTest, isTransformationRegistered) {
+ EXPECT_FALSE(isTransformationRegistered("not-a-transformation"));
+ EXPECT_TRUE(isTransformationRegistered("stub-transformation"));
+}
+
+TEST_F(TransformationRegistryTest, makeTransformation) {
+ WPASuite Suite = makeWPASuite();
+ StubEditEmitter Edits;
+ StubReportEmitter Report;
+ std::unique_ptr<Transformation> T =
+ makeTransformation("stub-transformation", Suite, Edits, Report);
+ EXPECT_NE(T, nullptr);
+}
+
+TEST_F(TransformationRegistryTest, EnumeratingRegistryEntries) {
+ auto Entries = TransformationRegistry::entries();
+ EXPECT_TRUE(llvm::any_of(Entries, [](const auto &Entry) {
+ return StringRef(Entry.getName()) == "stub-transformation";
+ }));
+}
+
+TEST_F(TransformationRegistryTest, PrintAvailableTransformations) {
+ std::string Buffer;
+ raw_string_ostream OS(Buffer);
+ printAvailableTransformations(OS);
+ EXPECT_NE(StringRef(Buffer).find("stub-transformation"), StringRef::npos);
+ EXPECT_NE(StringRef(Buffer).find("A transformation for testing"),
+ StringRef::npos);
+}
+
+} // namespace
More information about the cfe-commits
mailing list