[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
This commit is contained in:
1 parent
eb37dfcad1
commit
01846f68e5
5 files changed
+164
No files matched your search
+32
@@ -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
|
||||
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
|
||||
|
||||
add_clang_library(clangScalableStaticAnalysisSourceTransformation
|
||||
TransformationRegistry.cpp
|
||||
YAMLSourceEditFormat.cpp
|
||||
|
||||
LINK_LIBS
|
||||
clangAST
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
Reference in new issue
Block a user