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
