Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@@ -0,0 +1,19 @@
set(LLVM_LINK_COMPONENTS
Support
)
add_clang_library(clangApplyReplacements
lib/Tooling/ApplyReplacements.cpp
LINK_LIBS
clangAST
clangBasic
clangRewrite
clangToolingCore
)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
include
)
add_subdirectory(tool)

View File

@@ -0,0 +1,155 @@
//===-- ApplyReplacements.h - Deduplicate and apply replacements -- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file provides the interface for deduplicating, detecting
/// conflicts in, and applying collections of Replacements.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_APPLYREPLACEMENTS_H
#define LLVM_CLANG_APPLYREPLACEMENTS_H
#include "clang/Tooling/Core/Diagnostic.h"
#include "clang/Tooling/Refactoring.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include <string>
#include <system_error>
#include <vector>
namespace clang {
class DiagnosticsEngine;
class Rewriter;
namespace format {
struct FormatStyle;
} // end namespace format
namespace replace {
/// \brief Collection of source ranges.
typedef std::vector<clang::tooling::Range> RangeVector;
/// \brief Collection of TranslationUnitReplacements.
typedef std::vector<clang::tooling::TranslationUnitReplacements> TUReplacements;
/// \brief Collection of TranslationUnitReplacement files.
typedef std::vector<std::string> TUReplacementFiles;
/// \brief Collection of TranslationUniDiagnostics.
typedef std::vector<clang::tooling::TranslationUnitDiagnostics> TUDiagnostics;
/// \brief Map mapping file name to Replacements targeting that file.
typedef llvm::DenseMap<const clang::FileEntry *,
std::vector<clang::tooling::Replacement>>
FileToReplacementsMap;
/// \brief Recursively descends through a directory structure rooted at \p
/// Directory and attempts to deserialize *.yaml files as
/// TranslationUnitReplacements. All docs that successfully deserialize are
/// added to \p TUs.
///
/// Directories starting with '.' are ignored during traversal.
///
/// \param[in] Directory Directory to begin search for serialized
/// TranslationUnitReplacements.
/// \param[out] TUs Collection of all found and deserialized
/// TranslationUnitReplacements or TranslationUnitDiagnostics.
/// \param[out] TUFiles Collection of all TranslationUnitReplacement files
/// found in \c Directory.
/// \param[in] Diagnostics DiagnosticsEngine used for error output.
///
/// \returns An error_code indicating success or failure in navigating the
/// directory structure.
std::error_code collectReplacementsFromDirectory(
const llvm::StringRef Directory, TUReplacements &TUs,
TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics);
std::error_code collectReplacementsFromDirectory(
const llvm::StringRef Directory, TUDiagnostics &TUs,
TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics);
/// \brief Deduplicate, check for conflicts, and apply all Replacements stored
/// in \c TUs. If conflicts occur, no Replacements are applied.
///
/// \post For all (key,value) in GroupedReplacements, value[i].getOffset() <=
/// value[i+1].getOffset().
///
/// \param[in] TUs Collection of TranslationUnitReplacements or
/// TranslationUnitDiagnostics to merge,
/// deduplicate, and test for conflicts.
/// \param[out] GroupedReplacements Container grouping all Replacements by the
/// file they target.
/// \param[in] SM SourceManager required for conflict reporting.
///
/// \returns \parblock
/// \li true If all changes were applied successfully.
/// \li false If there were conflicts.
bool mergeAndDeduplicate(const TUReplacements &TUs,
FileToReplacementsMap &GroupedReplacements,
clang::SourceManager &SM);
bool mergeAndDeduplicate(const TUDiagnostics &TUs,
FileToReplacementsMap &GroupedReplacements,
clang::SourceManager &SM);
// FIXME: Remove this function after changing clang-apply-replacements to use
// Replacements class.
bool applyAllReplacements(const std::vector<tooling::Replacement> &Replaces,
Rewriter &Rewrite);
/// \brief Apply all replacements in \c GroupedReplacements.
///
/// \param[in] GroupedReplacements Deduplicated and conflict free Replacements
/// to apply.
/// \param[out] Rewrites The results of applying replacements will be applied
/// to this Rewriter.
///
/// \returns \parblock
/// \li true If all changes were applied successfully.
/// \li false If a replacement failed to apply.
bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
clang::Rewriter &Rewrites);
/// \brief Given a collection of Replacements for a single file, produces a list
/// of source ranges that enclose those Replacements.
///
/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
///
/// \param[in] Replacements Replacements from a single file.
///
/// \returns Collection of source ranges that enclose all given Replacements.
/// One range is created for each replacement.
RangeVector calculateChangedRanges(
const std::vector<clang::tooling::Replacement> &Replacements);
/// \brief Write the contents of \c FileContents to disk. Keys of the map are
/// filenames and values are the new contents for those files.
///
/// \param[in] Rewrites Rewriter containing written files to write to disk.
bool writeFiles(const clang::Rewriter &Rewrites);
/// \brief Delete the replacement files.
///
/// \param[in] Files Replacement files to delete.
/// \param[in] Diagnostics DiagnosticsEngine used for error output.
///
/// \returns \parblock
/// \li true If all files have been deleted successfully.
/// \li false If at least one or more failures occur when deleting
/// files.
bool deleteReplacementFiles(const TUReplacementFiles &Files,
clang::DiagnosticsEngine &Diagnostics);
} // end namespace replace
} // end namespace clang
#endif // LLVM_CLANG_APPLYREPLACEMENTS_H

View File

@@ -0,0 +1,400 @@
//===-- ApplyReplacements.cpp - Apply and deduplicate replacements --------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief 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/DiagnosticsYaml.h"
#include "clang/Tooling/ReplacementsYaml.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
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;
}
/// \brief Dumps information for a sequence of conflicting Replacements.
///
/// \param[in] File FileEntry for the file the conflicting Replacements are
/// for.
/// \param[in] ConflictingReplacements List of conflicting Replacements.
/// \param[in] SM SourceManager used for reporting.
static void reportConflict(
const FileEntry *File,
const llvm::ArrayRef<clang::tooling::Replacement> ConflictingReplacements,
SourceManager &SM) {
FileID FID = SM.translateFile(File);
if (FID.isInvalid())
FID = SM.createFileID(File, SourceLocation(), SrcMgr::C_User);
// FIXME: Output something a little more user-friendly (e.g. unified diff?)
errs() << "The following changes conflict:\n";
for (const tooling::Replacement &R : ConflictingReplacements) {
if (R.getLength() == 0) {
errs() << " Insert at " << SM.getLineNumber(FID, R.getOffset()) << ":"
<< SM.getColumnNumber(FID, R.getOffset()) << " "
<< R.getReplacementText() << "\n";
} else {
if (R.getReplacementText().empty())
errs() << " Remove ";
else
errs() << " Replace ";
errs() << SM.getLineNumber(FID, R.getOffset()) << ":"
<< SM.getColumnNumber(FID, R.getOffset()) << "-"
<< SM.getLineNumber(FID, R.getOffset() + R.getLength() - 1) << ":"
<< SM.getColumnNumber(FID, R.getOffset() + R.getLength() - 1);
if (R.getReplacementText().empty())
errs() << "\n";
else
errs() << " with \"" << R.getReplacementText() << "\"\n";
}
}
}
// FIXME: Remove this function after changing clang-apply-replacements to use
// Replacements class.
bool applyAllReplacements(const std::vector<tooling::Replacement> &Replaces,
Rewriter &Rewrite) {
bool Result = true;
for (auto I = Replaces.begin(), E = Replaces.end(); I != E; ++I) {
if (I->isApplicable()) {
Result = I->apply(Rewrite) && Result;
} else {
Result = false;
}
}
return Result;
}
// FIXME: moved from libToolingCore. remove this when std::vector<Replacement>
// is replaced with tooling::Replacements class.
static void deduplicate(std::vector<tooling::Replacement> &Replaces,
std::vector<tooling::Range> &Conflicts) {
if (Replaces.empty())
return;
auto LessNoPath = [](const tooling::Replacement &LHS,
const tooling::Replacement &RHS) {
if (LHS.getOffset() != RHS.getOffset())
return LHS.getOffset() < RHS.getOffset();
if (LHS.getLength() != RHS.getLength())
return LHS.getLength() < RHS.getLength();
return LHS.getReplacementText() < RHS.getReplacementText();
};
auto EqualNoPath = [](const tooling::Replacement &LHS,
const tooling::Replacement &RHS) {
return LHS.getOffset() == RHS.getOffset() &&
LHS.getLength() == RHS.getLength() &&
LHS.getReplacementText() == RHS.getReplacementText();
};
// Deduplicate. We don't want to deduplicate based on the path as we assume
// that all replacements refer to the same file (or are symlinks).
std::sort(Replaces.begin(), Replaces.end(), LessNoPath);
Replaces.erase(std::unique(Replaces.begin(), Replaces.end(), EqualNoPath),
Replaces.end());
// Detect conflicts
tooling::Range ConflictRange(Replaces.front().getOffset(),
Replaces.front().getLength());
unsigned ConflictStart = 0;
unsigned ConflictLength = 1;
for (unsigned i = 1; i < Replaces.size(); ++i) {
tooling::Range Current(Replaces[i].getOffset(), Replaces[i].getLength());
if (ConflictRange.overlapsWith(Current)) {
// Extend conflicted range
ConflictRange =
tooling::Range(ConflictRange.getOffset(),
std::max(ConflictRange.getLength(),
Current.getOffset() + Current.getLength() -
ConflictRange.getOffset()));
++ConflictLength;
} else {
if (ConflictLength > 1)
Conflicts.push_back(tooling::Range(ConflictStart, ConflictLength));
ConflictRange = Current;
ConflictStart = i;
ConflictLength = 1;
}
}
if (ConflictLength > 1)
Conflicts.push_back(tooling::Range(ConflictStart, ConflictLength));
}
/// \brief Deduplicates and tests for conflicts among the replacements for each
/// file in \c Replacements. Any conflicts found are reported.
///
/// \post Replacements[i].getOffset() <= Replacements[i+1].getOffset().
///
/// \param[in,out] Replacements Container of all replacements grouped by file
/// to be deduplicated and checked for conflicts.
/// \param[in] SM SourceManager required for conflict reporting.
///
/// \returns \parblock
/// \li true if conflicts were detected
/// \li false if no conflicts were detected
static bool deduplicateAndDetectConflicts(FileToReplacementsMap &Replacements,
SourceManager &SM) {
bool conflictsFound = false;
for (auto &FileAndReplacements : Replacements) {
const FileEntry *Entry = FileAndReplacements.first;
auto &Replacements = FileAndReplacements.second;
assert(Entry != nullptr && "No file entry!");
std::vector<tooling::Range> Conflicts;
deduplicate(FileAndReplacements.second, Conflicts);
if (Conflicts.empty())
continue;
conflictsFound = true;
errs() << "There are conflicting changes to " << Entry->getName() << ":\n";
for (const tooling::Range &Conflict : Conflicts) {
auto ConflictingReplacements = llvm::makeArrayRef(
&Replacements[Conflict.getOffset()], Conflict.getLength());
reportConflict(Entry, ConflictingReplacements, SM);
}
}
return conflictsFound;
}
bool mergeAndDeduplicate(const TUReplacements &TUs,
FileToReplacementsMap &GroupedReplacements,
clang::SourceManager &SM) {
// Group all replacements by target file.
std::set<StringRef> Warned;
for (const auto &TU : TUs) {
for (const tooling::Replacement &R : TU.Replacements) {
// Use the file manager to deduplicate paths. FileEntries are
// automatically canonicalized.
if (const FileEntry *Entry = SM.getFileManager().getFile(R.getFilePath())) {
GroupedReplacements[Entry].push_back(R);
} else if (Warned.insert(R.getFilePath()).second) {
errs() << "Described file '" << R.getFilePath()
<< "' doesn't exist. Ignoring...\n";
}
}
}
// Ask clang to deduplicate and report conflicts.
return !deduplicateAndDetectConflicts(GroupedReplacements, SM);
}
bool mergeAndDeduplicate(const TUDiagnostics &TUs,
FileToReplacementsMap &GroupedReplacements,
clang::SourceManager &SM) {
// Group all replacements by target file.
std::set<StringRef> Warned;
for (const auto &TU : TUs) {
for (const auto &D : TU.Diagnostics) {
for (const auto &Fix : D.Fix) {
for (const tooling::Replacement &R : Fix.second) {
// Use the file manager to deduplicate paths. FileEntries are
// automatically canonicalized.
if (const FileEntry *Entry = SM.getFileManager().getFile(R.getFilePath())) {
GroupedReplacements[Entry].push_back(R);
} else if (Warned.insert(R.getFilePath()).second) {
errs() << "Described file '" << R.getFilePath()
<< "' doesn't exist. Ignoring...\n";
}
}
}
}
}
// Ask clang to deduplicate and report conflicts.
return !deduplicateAndDetectConflicts(GroupedReplacements, SM);
}
bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
clang::Rewriter &Rewrites) {
// Apply all changes
//
// FIXME: No longer certain GroupedReplacements is really the best kind of
// data structure for applying replacements. Rewriter certainly doesn't care.
// However, until we nail down the design of ReplacementGroups, might as well
// leave this as is.
for (const auto &FileAndReplacements : GroupedReplacements) {
if (!applyAllReplacements(FileAndReplacements.second, Rewrites))
return false;
}
return true;
}
RangeVector calculateChangedRanges(
const std::vector<clang::tooling::Replacement> &Replaces) {
RangeVector ChangedRanges;
// Generate the new ranges from the replacements.
int Shift = 0;
for (const tooling::Replacement &R : Replaces) {
unsigned Offset = R.getOffset() + Shift;
unsigned Length = R.getReplacementText().size();
Shift += Length - R.getLength();
ChangedRanges.push_back(tooling::Range(Offset, Length));
}
return ChangedRanges;
}
bool writeFiles(const clang::Rewriter &Rewrites) {
for (auto BufferI = Rewrites.buffer_begin(), BufferE = Rewrites.buffer_end();
BufferI != BufferE; ++BufferI) {
StringRef FileName =
Rewrites.getSourceMgr().getFileEntryForID(BufferI->first)->getName();
std::error_code EC;
llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_Text);
if (EC) {
errs() << "Warning: Could not write to " << EC.message() << "\n";
continue;
}
BufferI->second.write(FileStream);
}
return true;
}
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

View File

@@ -0,0 +1,18 @@
set(LLVM_LINK_COMPONENTS
Support
)
add_clang_executable(clang-apply-replacements
ClangApplyReplacementsMain.cpp
)
target_link_libraries(clang-apply-replacements
PRIVATE
clangApplyReplacements
clangBasic
clangFormat
clangRewrite
clangToolingCore
)
install(TARGETS clang-apply-replacements
RUNTIME DESTINATION bin)

View File

@@ -0,0 +1,290 @@
//===-- ClangApplyReplacementsMain.cpp - Main file for the tool -----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file provides the main function for the
/// clang-apply-replacements tool.
///
//===----------------------------------------------------------------------===//
#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Version.h"
#include "clang/Format/Format.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/CommandLine.h"
using namespace llvm;
using namespace clang;
using namespace clang::replace;
static cl::opt<std::string> Directory(cl::Positional, cl::Required,
cl::desc("<Search Root Directory>"));
static cl::OptionCategory ReplacementCategory("Replacement Options");
static cl::OptionCategory FormattingCategory("Formatting Options");
const cl::OptionCategory *VisibleCategories[] = {&ReplacementCategory,
&FormattingCategory};
static cl::opt<bool> RemoveTUReplacementFiles(
"remove-change-desc-files",
cl::desc("Remove the change description files regardless of successful\n"
"merging/replacing."),
cl::init(false), cl::cat(ReplacementCategory));
static cl::opt<bool> DoFormat(
"format",
cl::desc("Enable formatting of code changed by applying replacements.\n"
"Use -style to choose formatting style.\n"),
cl::cat(FormattingCategory));
// FIXME: Consider making the default behaviour for finding a style
// configuration file to start the search anew for every file being changed to
// handle situations where the style is different for different parts of a
// project.
static cl::opt<std::string> FormatStyleConfig(
"style-config",
cl::desc("Path to a directory containing a .clang-format file\n"
"describing a formatting style to use for formatting\n"
"code when -style=file.\n"),
cl::init(""), cl::cat(FormattingCategory));
static cl::opt<std::string>
FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
cl::init("LLVM"), cl::cat(FormattingCategory));
namespace {
// Helper object to remove the TUReplacement and TUDiagnostic (triggered by
// "remove-change-desc-files" command line option) when exiting current scope.
class ScopedFileRemover {
public:
ScopedFileRemover(const TUReplacementFiles &Files,
clang::DiagnosticsEngine &Diagnostics)
: TURFiles(Files), Diag(Diagnostics) {}
~ScopedFileRemover() { deleteReplacementFiles(TURFiles, Diag); }
private:
const TUReplacementFiles &TURFiles;
clang::DiagnosticsEngine &Diag;
};
} // namespace
static void printVersion(raw_ostream &OS) {
OS << "clang-apply-replacements version " CLANG_VERSION_STRING << "\n";
}
/// \brief Convenience function to get rewritten content for \c Filename from
/// \c Rewrites.
///
/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
/// \post Replacements.empty() -> Result.empty()
///
/// \param[in] Replacements Replacements to apply
/// \param[in] Rewrites Rewriter to use to apply replacements.
/// \param[out] Result Contents of the file after applying replacements if
/// replacements were provided.
///
/// \returns \parblock
/// \li true if all replacements were applied successfully.
/// \li false if at least one replacement failed to apply.
static bool
getRewrittenData(const std::vector<tooling::Replacement> &Replacements,
Rewriter &Rewrites, std::string &Result) {
if (Replacements.empty())
return true;
if (!applyAllReplacements(Replacements, Rewrites))
return false;
SourceManager &SM = Rewrites.getSourceMgr();
FileManager &Files = SM.getFileManager();
StringRef FileName = Replacements.begin()->getFilePath();
const clang::FileEntry *Entry = Files.getFile(FileName);
assert(Entry && "Expected an existing file");
FileID ID = SM.translateFile(Entry);
assert(ID.isValid() && "Expected a valid FileID");
const RewriteBuffer *Buffer = Rewrites.getRewriteBufferFor(ID);
Result = std::string(Buffer->begin(), Buffer->end());
return true;
}
/// \brief Apply \c Replacements and return the new file contents.
///
/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
/// \post Replacements.empty() -> Result.empty()
///
/// \param[in] Replacements Replacements to apply.
/// \param[out] Result Contents of the file after applying replacements if
/// replacements were provided.
/// \param[in] Diagnostics For diagnostic output.
///
/// \returns \parblock
/// \li true if all replacements applied successfully.
/// \li false if at least one replacement failed to apply.
static bool
applyReplacements(const std::vector<tooling::Replacement> &Replacements,
std::string &Result, DiagnosticsEngine &Diagnostics) {
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
Rewriter Rewrites(SM, LangOptions());
return getRewrittenData(Replacements, Rewrites, Result);
}
/// \brief Apply code formatting to all places where replacements were made.
///
/// \pre !Replacements.empty().
/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
///
/// \param[in] Replacements Replacements that were made to the file. Provided
/// to indicate where changes were made.
/// \param[in] FileData The contents of the file \b after \c Replacements have
/// been applied.
/// \param[out] FormattedFileData The contents of the file after reformatting.
/// \param[in] FormatStyle Style to apply.
/// \param[in] Diagnostics For diagnostic output.
///
/// \returns \parblock
/// \li true if reformatting replacements were all successfully
/// applied.
/// \li false if at least one reformatting replacement failed to apply.
static bool
applyFormatting(const std::vector<tooling::Replacement> &Replacements,
const StringRef FileData, std::string &FormattedFileData,
const format::FormatStyle &FormatStyle,
DiagnosticsEngine &Diagnostics) {
assert(!Replacements.empty() && "Need at least one replacement");
RangeVector Ranges = calculateChangedRanges(Replacements);
StringRef FileName = Replacements.begin()->getFilePath();
tooling::Replacements R =
format::reformat(FormatStyle, FileData, Ranges, FileName);
// FIXME: Remove this copy when tooling::Replacements is implemented as a
// vector instead of a set.
std::vector<tooling::Replacement> FormattingReplacements;
std::copy(R.begin(), R.end(), back_inserter(FormattingReplacements));
if (FormattingReplacements.empty()) {
FormattedFileData = FileData;
return true;
}
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
SM.overrideFileContents(Files.getFile(FileName),
llvm::MemoryBuffer::getMemBufferCopy(FileData));
Rewriter Rewrites(SM, LangOptions());
return getRewrittenData(FormattingReplacements, Rewrites, FormattedFileData);
}
int main(int argc, char **argv) {
cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories));
cl::SetVersionPrinter(printVersion);
cl::ParseCommandLineOptions(argc, argv);
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), DiagOpts.get());
// Determine a formatting style from options.
format::FormatStyle FormatStyle;
if (DoFormat) {
auto FormatStyleOrError =
format::getStyle(FormatStyleOpt, FormatStyleConfig, "LLVM");
if (!FormatStyleOrError) {
llvm::errs() << llvm::toString(FormatStyleOrError.takeError()) << "\n";
return 1;
}
FormatStyle = *FormatStyleOrError;
}
TUReplacements TURs;
TUReplacementFiles TUFiles;
std::error_code ErrorCode =
collectReplacementsFromDirectory(Directory, TURs, TUFiles, Diagnostics);
TUDiagnostics TUDs;
TUFiles.clear();
ErrorCode =
collectReplacementsFromDirectory(Directory, TUDs, TUFiles, Diagnostics);
if (ErrorCode) {
errs() << "Trouble iterating over directory '" << Directory
<< "': " << ErrorCode.message() << "\n";
return 1;
}
// Remove the TUReplacementFiles (triggered by "remove-change-desc-files"
// command line option) when exiting main().
std::unique_ptr<ScopedFileRemover> Remover;
if (RemoveTUReplacementFiles)
Remover.reset(new ScopedFileRemover(TUFiles, Diagnostics));
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
FileToReplacementsMap GroupedReplacements;
if (!mergeAndDeduplicate(TURs, GroupedReplacements, SM))
return 1;
if (!mergeAndDeduplicate(TUDs, GroupedReplacements, SM))
return 1;
Rewriter ReplacementsRewriter(SM, LangOptions());
for (const auto &FileAndReplacements : GroupedReplacements) {
// This shouldn't happen but if a file somehow has no replacements skip to
// next file.
if (FileAndReplacements.second.empty())
continue;
std::string NewFileData;
StringRef FileName = FileAndReplacements.first->getName();
if (!applyReplacements(FileAndReplacements.second, NewFileData,
Diagnostics)) {
errs() << "Failed to apply replacements to " << FileName << "\n";
continue;
}
// Apply formatting if requested.
if (DoFormat &&
!applyFormatting(FileAndReplacements.second, NewFileData, NewFileData,
FormatStyle, Diagnostics)) {
errs() << "Failed to apply reformatting replacements for " << FileName
<< "\n";
continue;
}
// Write new file to disk
std::error_code EC;
llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_None);
if (EC) {
llvm::errs() << "Could not open " << FileName << " for writing\n";
continue;
}
FileStream << NewFileData;
}
return 0;
}