Author: Jan Korous
Date: 2026-07-08T10:53:15-07:00
New Revision: 01846f68e5ae612fd348784265e14c0130cd79fd

URL: 
https://github.com/llvm/llvm-project/commit/01846f68e5ae612fd348784265e14c0130cd79fd
DIFF: 
https://github.com/llvm/llvm-project/commit/01846f68e5ae612fd348784265e14c0130cd79fd.diff

LOG: [clang][ssaf] Add YAML source-edit format (#204491)

Adds the built-in `SourceEditFormat`, registered under the file
extension `yaml`. The writer drives `llvm::yaml::Output` against the
existing `clang::tooling::TranslationUnitReplacements` `MappingTraits`
from `clang/Tooling/ReplacementsYaml.h`, so the resulting document is
byte-for-byte consumable by `clang-apply-replacements`.

Anchored via `SSAFYAMLSourceEditFormatAnchorSource` so static builds
keep the registration.

Assisted-By: Claude Opus 4.7

Added: 
    
clang/include/clang/ScalableStaticAnalysis/SourceTransformation/YAMLSourceEditFormat.h
    
clang/lib/ScalableStaticAnalysis/SourceTransformation/YAMLSourceEditFormat.cpp
    
clang/unittests/ScalableStaticAnalysis/SourceTransformation/YAMLFormatTest.cpp

Modified: 
    clang/lib/ScalableStaticAnalysis/SourceTransformation/CMakeLists.txt
    clang/unittests/ScalableStaticAnalysis/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git 
a/clang/include/clang/ScalableStaticAnalysis/SourceTransformation/YAMLSourceEditFormat.h
 
b/clang/include/clang/ScalableStaticAnalysis/SourceTransformation/YAMLSourceEditFormat.h
new file mode 100644
index 0000000000000..0676d6b361158
--- /dev/null
+++ 
b/clang/include/clang/ScalableStaticAnalysis/SourceTransformation/YAMLSourceEditFormat.h
@@ -0,0 +1,32 @@
+//===- YAMLSourceEditFormat.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Built-in YAML source-edit writer. The on-disk layout is the existing
+// `clang::tooling::TranslationUnitReplacements` YAML schema, byte-for-byte
+// consumable by `clang-apply-replacements`.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef 
LLVM_CLANG_SCALABLESTATICANALYSIS_SOURCETRANSFORMATION_YAMLSOURCEEDITFORMAT_H
+#define 
LLVM_CLANG_SCALABLESTATICANALYSIS_SOURCETRANSFORMATION_YAMLSOURCEEDITFORMAT_H
+
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+
+namespace clang::ssaf {
+
+/// Writes \p Doc to \p Path as a YAML document compatible with
+/// `clang-apply-replacements`.
+llvm::Error
+writeYAMLSourceEdits(const clang::tooling::TranslationUnitReplacements &Doc,
+                     llvm::StringRef Path);
+
+} // namespace clang::ssaf
+
+#endif // 
LLVM_CLANG_SCALABLESTATICANALYSIS_SOURCETRANSFORMATION_YAMLSOURCEEDITFORMAT_H

diff  --git 
a/clang/lib/ScalableStaticAnalysis/SourceTransformation/CMakeLists.txt 
b/clang/lib/ScalableStaticAnalysis/SourceTransformation/CMakeLists.txt
index 3761ee7463f86..f46b6df1d0bdb 100644
--- a/clang/lib/ScalableStaticAnalysis/SourceTransformation/CMakeLists.txt
+++ b/clang/lib/ScalableStaticAnalysis/SourceTransformation/CMakeLists.txt
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
 
 add_clang_library(clangScalableStaticAnalysisSourceTransformation
   TransformationRegistry.cpp
+  YAMLSourceEditFormat.cpp
 
   LINK_LIBS
   clangAST

diff  --git 
a/clang/lib/ScalableStaticAnalysis/SourceTransformation/YAMLSourceEditFormat.cpp
 
b/clang/lib/ScalableStaticAnalysis/SourceTransformation/YAMLSourceEditFormat.cpp
new file mode 100644
index 0000000000000..3bdbdb148e5cd
--- /dev/null
+++ 
b/clang/lib/ScalableStaticAnalysis/SourceTransformation/YAMLSourceEditFormat.cpp
@@ -0,0 +1,33 @@
+//===- YAMLSourceEditFormat.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/ScalableStaticAnalysis/SourceTransformation/YAMLSourceEditFormat.h"
+#include "clang/Tooling/ReplacementsYaml.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ssaf;
+
+llvm::Error ssaf::writeYAMLSourceEdits(
+    const clang::tooling::TranslationUnitReplacements &Doc,
+    llvm::StringRef Path) {
+  std::error_code EC;
+  llvm::raw_fd_ostream OS(Path, EC, llvm::sys::fs::OF_None);
+  if (EC)
+    return llvm::createStringError(EC, "failed to open '" + Path + "'");
+
+  // llvm::yaml::Output's stream operator binds to a non-const reference.
+  clang::tooling::TranslationUnitReplacements Mutable = Doc;
+  llvm::yaml::Output YAMLOut(OS);
+  YAMLOut << Mutable;
+
+  return llvm::Error::success();
+}

diff  --git a/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt 
b/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt
index 387f03dc76fc6..12ee90f3cbebf 100644
--- a/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt
+++ b/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt
@@ -26,6 +26,7 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests
   Serialization/JSONFormatTest/TUSummaryTest.cpp
   SourceTransformation/EmitterTest.cpp
   SourceTransformation/RegistryTest.cpp
+  SourceTransformation/YAMLFormatTest.cpp
   SummaryData/SummaryDataTest.cpp
   SummaryNameTest.cpp
   TestFixture.cpp

diff  --git 
a/clang/unittests/ScalableStaticAnalysis/SourceTransformation/YAMLFormatTest.cpp
 
b/clang/unittests/ScalableStaticAnalysis/SourceTransformation/YAMLFormatTest.cpp
new file mode 100644
index 0000000000000..d654a0436ae8b
--- /dev/null
+++ 
b/clang/unittests/ScalableStaticAnalysis/SourceTransformation/YAMLFormatTest.cpp
@@ -0,0 +1,97 @@
+//===- YAMLFormatTest.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/ScalableStaticAnalysis/SourceTransformation/YAMLSourceEditFormat.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/ReplacementsYaml.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+using namespace ssaf;
+
+namespace {
+
+// Materializes a unique temporary file path under the system temp dir and
+// removes it on destruction.
+struct TempPath {
+  SmallString<128> Path;
+
+  TempPath(StringRef Suffix) {
+    sys::fs::createUniquePath("ssaf-yaml-%%%%%%." + Suffix, Path,
+                              /*MakeAbsolute=*/true);
+  }
+  ~TempPath() { sys::fs::remove(Path); }
+};
+
+TEST(WriteYAMLSourceEditsTest, RoundTripsTwoReplacements) {
+  clang::tooling::TranslationUnitReplacements Doc;
+  Doc.MainSourceFile = "main.cpp";
+  Doc.Replacements.emplace_back("a.cpp", 0, 0, "/*1*/");
+  Doc.Replacements.emplace_back("b.cpp", 10, 3, "/*2*/");
+
+  TempPath TP("yaml");
+  ASSERT_THAT_ERROR(writeYAMLSourceEdits(Doc, TP.Path), Succeeded());
+
+  auto BufferOrErr = MemoryBuffer::getFile(TP.Path);
+  ASSERT_TRUE(static_cast<bool>(BufferOrErr))
+      << "Failed to read back '" << TP.Path << "'";
+
+  clang::tooling::TranslationUnitReplacements Parsed;
+  yaml::Input YIn((*BufferOrErr)->getBuffer());
+  YIn >> Parsed;
+  ASSERT_FALSE(YIn.error()) << YIn.error().message();
+
+  EXPECT_EQ(Parsed.MainSourceFile, "main.cpp");
+  ASSERT_EQ(Parsed.Replacements.size(), 2u);
+  EXPECT_EQ(Parsed.Replacements[0].getFilePath(), "a.cpp");
+  EXPECT_EQ(Parsed.Replacements[0].getOffset(), 0u);
+  EXPECT_EQ(Parsed.Replacements[0].getLength(), 0u);
+  EXPECT_EQ(Parsed.Replacements[0].getReplacementText(), "/*1*/");
+  EXPECT_EQ(Parsed.Replacements[1].getFilePath(), "b.cpp");
+  EXPECT_EQ(Parsed.Replacements[1].getOffset(), 10u);
+  EXPECT_EQ(Parsed.Replacements[1].getLength(), 3u);
+  EXPECT_EQ(Parsed.Replacements[1].getReplacementText(), "/*2*/");
+}
+
+TEST(WriteYAMLSourceEditsTest, EmptyReplacementsWritesValidDocument) {
+  clang::tooling::TranslationUnitReplacements Doc;
+  Doc.MainSourceFile = "main.cpp";
+
+  TempPath TP("yaml");
+  ASSERT_THAT_ERROR(writeYAMLSourceEdits(Doc, TP.Path), Succeeded());
+
+  auto BufferOrErr = MemoryBuffer::getFile(TP.Path);
+  ASSERT_TRUE(static_cast<bool>(BufferOrErr));
+
+  clang::tooling::TranslationUnitReplacements Parsed;
+  yaml::Input YIn((*BufferOrErr)->getBuffer());
+  YIn >> Parsed;
+  ASSERT_FALSE(YIn.error()) << YIn.error().message();
+  EXPECT_EQ(Parsed.MainSourceFile, "main.cpp");
+  EXPECT_TRUE(Parsed.Replacements.empty());
+}
+
+TEST(WriteYAMLSourceEditsTest, OpenErrorReturnsError) {
+  clang::tooling::TranslationUnitReplacements Doc;
+  Doc.MainSourceFile = "main.cpp";
+
+  // Path under a directory that does not exist.
+  SmallString<128> BadPath;
+  sys::fs::createUniquePath("ssaf-missing-%%%%%%/edits.yaml", BadPath,
+                            /*MakeAbsolute=*/true);
+
+  ASSERT_THAT_ERROR(writeYAMLSourceEdits(Doc, BadPath), Failed());
+}
+
+} // namespace


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

Reply via email to