https://github.com/jkorous-apple created 
https://github.com/llvm/llvm-project/pull/204214

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

>From 577a6f69afd9d3aa4745a9a0974ef1b010bc8df5 Mon Sep 17 00:00:00 2001
From: Jan Korous <[email protected]>
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

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to