diff options
author | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 13:58:24 +0300 |
---|---|---|
committer | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 14:11:53 +0300 |
commit | 11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch) | |
tree | fabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/tools/extra/clang-apply-replacements/lib/Tooling | |
parent | 9685917341315774aad5733b1793b1e533a88bbb (diff) | |
download | ydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz |
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/tools/extra/clang-apply-replacements/lib/Tooling')
-rw-r--r-- | contrib/libs/clang16/tools/extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp | 296 | ||||
-rw-r--r-- | contrib/libs/clang16/tools/extra/clang-apply-replacements/lib/Tooling/ya.make | 34 |
2 files changed, 330 insertions, 0 deletions
diff --git a/contrib/libs/clang16/tools/extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp b/contrib/libs/clang16/tools/extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp new file mode 100644 index 0000000000..a0a510c201 --- /dev/null +++ b/contrib/libs/clang16/tools/extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp @@ -0,0 +1,296 @@ +//===-- ApplyReplacements.cpp - Apply and deduplicate replacements --------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file provides the implementation for deduplicating, detecting +/// conflicts in, and applying collections of Replacements. +/// +/// FIXME: Use Diagnostics for output instead of llvm::errs(). +/// +//===----------------------------------------------------------------------===// +#include "clang-apply-replacements/Tooling/ApplyReplacements.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Format/Format.h" +#include "clang/Lex/Lexer.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Tooling/Core/Diagnostic.h" +#include "clang/Tooling/DiagnosticsYaml.h" +#include "clang/Tooling/ReplacementsYaml.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <optional> + +using namespace llvm; +using namespace clang; + +static void eatDiagnostics(const SMDiagnostic &, void *) {} + +namespace clang { +namespace replace { + +std::error_code collectReplacementsFromDirectory( + const llvm::StringRef Directory, TUReplacements &TUs, + TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics) { + using namespace llvm::sys::fs; + using namespace llvm::sys::path; + + std::error_code ErrorCode; + + for (recursive_directory_iterator I(Directory, ErrorCode), E; + I != E && !ErrorCode; I.increment(ErrorCode)) { + if (filename(I->path())[0] == '.') { + // Indicate not to descend into directories beginning with '.' + I.no_push(); + continue; + } + + if (extension(I->path()) != ".yaml") + continue; + + TUFiles.push_back(I->path()); + + ErrorOr<std::unique_ptr<MemoryBuffer>> Out = + MemoryBuffer::getFile(I->path()); + if (std::error_code BufferError = Out.getError()) { + errs() << "Error reading " << I->path() << ": " << BufferError.message() + << "\n"; + continue; + } + + yaml::Input YIn(Out.get()->getBuffer(), nullptr, &eatDiagnostics); + tooling::TranslationUnitReplacements TU; + YIn >> TU; + if (YIn.error()) { + // File doesn't appear to be a header change description. Ignore it. + continue; + } + + // Only keep files that properly parse. + TUs.push_back(TU); + } + + return ErrorCode; +} + +std::error_code collectReplacementsFromDirectory( + const llvm::StringRef Directory, TUDiagnostics &TUs, + TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics) { + using namespace llvm::sys::fs; + using namespace llvm::sys::path; + + std::error_code ErrorCode; + + for (recursive_directory_iterator I(Directory, ErrorCode), E; + I != E && !ErrorCode; I.increment(ErrorCode)) { + if (filename(I->path())[0] == '.') { + // Indicate not to descend into directories beginning with '.' + I.no_push(); + continue; + } + + if (extension(I->path()) != ".yaml") + continue; + + TUFiles.push_back(I->path()); + + ErrorOr<std::unique_ptr<MemoryBuffer>> Out = + MemoryBuffer::getFile(I->path()); + if (std::error_code BufferError = Out.getError()) { + errs() << "Error reading " << I->path() << ": " << BufferError.message() + << "\n"; + continue; + } + + yaml::Input YIn(Out.get()->getBuffer(), nullptr, &eatDiagnostics); + tooling::TranslationUnitDiagnostics TU; + YIn >> TU; + if (YIn.error()) { + // File doesn't appear to be a header change description. Ignore it. + continue; + } + + // Only keep files that properly parse. + TUs.push_back(TU); + } + + return ErrorCode; +} + +/// Extract replacements from collected TranslationUnitReplacements and +/// TranslationUnitDiagnostics and group them per file. Identical replacements +/// from diagnostics are deduplicated. +/// +/// \param[in] TUs Collection of all found and deserialized +/// TranslationUnitReplacements. +/// \param[in] TUDs Collection of all found and deserialized +/// TranslationUnitDiagnostics. +/// \param[in] SM Used to deduplicate paths. +/// +/// \returns A map mapping FileEntry to a set of Replacement targeting that +/// file. +static llvm::DenseMap<const FileEntry *, std::vector<tooling::Replacement>> +groupReplacements(const TUReplacements &TUs, const TUDiagnostics &TUDs, + const clang::SourceManager &SM) { + llvm::StringSet<> Warned; + llvm::DenseMap<const FileEntry *, std::vector<tooling::Replacement>> + GroupedReplacements; + + // Deduplicate identical replacements in diagnostics unless they are from the + // same TU. + // FIXME: Find an efficient way to deduplicate on diagnostics level. + llvm::DenseMap<const FileEntry *, + std::map<tooling::Replacement, + const tooling::TranslationUnitDiagnostics *>> + DiagReplacements; + + auto AddToGroup = [&](const tooling::Replacement &R, + const tooling::TranslationUnitDiagnostics *SourceTU, + const std::optional<std::string> BuildDir) { + // Use the file manager to deduplicate paths. FileEntries are + // automatically canonicalized. Since relative paths can come from different + // build directories, make them absolute immediately. + SmallString<128> Path = R.getFilePath(); + if (BuildDir) + llvm::sys::fs::make_absolute(*BuildDir, Path); + else + SM.getFileManager().makeAbsolutePath(Path); + + if (auto Entry = SM.getFileManager().getFile(Path)) { + if (SourceTU) { + auto &Replaces = DiagReplacements[*Entry]; + auto It = Replaces.find(R); + if (It == Replaces.end()) + Replaces.emplace(R, SourceTU); + else if (It->second != SourceTU) + // This replacement is a duplicate of one suggested by another TU. + return; + } + GroupedReplacements[*Entry].push_back(R); + } else if (Warned.insert(Path).second) { + errs() << "Described file '" << R.getFilePath() + << "' doesn't exist. Ignoring...\n"; + } + }; + + for (const auto &TU : TUs) + for (const tooling::Replacement &R : TU.Replacements) + AddToGroup(R, nullptr, {}); + + for (const auto &TU : TUDs) + for (const auto &D : TU.Diagnostics) + if (const auto *ChoosenFix = tooling::selectFirstFix(D)) { + for (const auto &Fix : *ChoosenFix) + for (const tooling::Replacement &R : Fix.second) + AddToGroup(R, &TU, D.BuildDirectory); + } + + // Sort replacements per file to keep consistent behavior when + // clang-apply-replacements run on differents machine. + for (auto &FileAndReplacements : GroupedReplacements) { + llvm::sort(FileAndReplacements.second); + } + + return GroupedReplacements; +} + +bool mergeAndDeduplicate(const TUReplacements &TUs, const TUDiagnostics &TUDs, + FileToChangesMap &FileChanges, + clang::SourceManager &SM, bool IgnoreInsertConflict) { + auto GroupedReplacements = groupReplacements(TUs, TUDs, SM); + bool ConflictDetected = false; + + // To report conflicting replacements on corresponding file, all replacements + // are stored into 1 big AtomicChange. + for (const auto &FileAndReplacements : GroupedReplacements) { + const FileEntry *Entry = FileAndReplacements.first; + const SourceLocation BeginLoc = + SM.getLocForStartOfFile(SM.getOrCreateFileID(Entry, SrcMgr::C_User)); + tooling::AtomicChange FileChange(Entry->getName(), Entry->getName()); + for (const auto &R : FileAndReplacements.second) { + llvm::Error Err = + FileChange.replace(SM, BeginLoc.getLocWithOffset(R.getOffset()), + R.getLength(), R.getReplacementText()); + if (Err) { + // FIXME: This will report conflicts by pair using a file+offset format + // which is not so much human readable. + // A first improvement could be to translate offset to line+col. For + // this and without loosing error message some modifications around + // `tooling::ReplacementError` are need (access to + // `getReplacementErrString`). + // A better strategy could be to add a pretty printer methods for + // conflict reporting. Methods that could be parameterized to report a + // conflict in different format, file+offset, file+line+col, or even + // more human readable using VCS conflict markers. + // For now, printing directly the error reported by `AtomicChange` is + // the easiest solution. + errs() << llvm::toString(std::move(Err)) << "\n"; + if (IgnoreInsertConflict) { + tooling::Replacements &Replacements = FileChange.getReplacements(); + unsigned NewOffset = + Replacements.getShiftedCodePosition(R.getOffset()); + unsigned NewLength = Replacements.getShiftedCodePosition( + R.getOffset() + R.getLength()) - + NewOffset; + if (NewLength == R.getLength()) { + tooling::Replacement RR = tooling::Replacement( + R.getFilePath(), NewOffset, NewLength, R.getReplacementText()); + Replacements = Replacements.merge(tooling::Replacements(RR)); + } else { + llvm::errs() + << "Can't resolve conflict, skipping the replacement.\n"; + ConflictDetected = true; + } + } else + ConflictDetected = true; + } + } + FileChanges.try_emplace(Entry, + std::vector<tooling::AtomicChange>{FileChange}); + } + + return !ConflictDetected; +} + +llvm::Expected<std::string> +applyChanges(StringRef File, const std::vector<tooling::AtomicChange> &Changes, + const tooling::ApplyChangesSpec &Spec, + DiagnosticsEngine &Diagnostics) { + FileManager Files((FileSystemOptions())); + SourceManager SM(Diagnostics, Files); + + llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer = + SM.getFileManager().getBufferForFile(File); + if (!Buffer) + return errorCodeToError(Buffer.getError()); + return tooling::applyAtomicChanges(File, Buffer.get()->getBuffer(), Changes, + Spec); +} + +bool deleteReplacementFiles(const TUReplacementFiles &Files, + clang::DiagnosticsEngine &Diagnostics) { + bool Success = true; + for (const auto &Filename : Files) { + std::error_code Error = llvm::sys::fs::remove(Filename); + if (Error) { + Success = false; + // FIXME: Use Diagnostics for outputting errors. + errs() << "Error deleting file: " << Filename << "\n"; + errs() << Error.message() << "\n"; + errs() << "Please delete the file manually\n"; + } + } + return Success; +} + +} // end namespace replace +} // end namespace clang diff --git a/contrib/libs/clang16/tools/extra/clang-apply-replacements/lib/Tooling/ya.make b/contrib/libs/clang16/tools/extra/clang-apply-replacements/lib/Tooling/ya.make new file mode 100644 index 0000000000..ced2888f96 --- /dev/null +++ b/contrib/libs/clang16/tools/extra/clang-apply-replacements/lib/Tooling/ya.make @@ -0,0 +1,34 @@ +# Generated by devtools/yamaker. + +LIBRARY() + +LICENSE(Apache-2.0 WITH LLVM-exception) + +LICENSE_TEXTS(.yandex_meta/licenses.list.txt) + +PEERDIR( + contrib/libs/clang16 + contrib/libs/clang16/include + contrib/libs/clang16/lib/AST + contrib/libs/clang16/lib/Basic + contrib/libs/clang16/lib/Rewrite + contrib/libs/clang16/lib/Tooling/Core + contrib/libs/clang16/lib/Tooling/Refactoring + contrib/libs/llvm16 + contrib/libs/llvm16/lib/Support +) + +ADDINCL( + contrib/libs/clang16/tools/extra/clang-apply-replacements + contrib/libs/clang16/tools/extra/clang-apply-replacements/include +) + +NO_COMPILER_WARNINGS() + +NO_UTIL() + +SRCS( + ApplyReplacements.cpp +) + +END() |