169 lines · cpp
1//===-- ClangApplyReplacementsMain.cpp - Main file for the tool -----------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file provides the main function for the11/// clang-apply-replacements tool.12///13//===----------------------------------------------------------------------===//14 15#include "clang-apply-replacements/Tooling/ApplyReplacements.h"16#include "clang/Basic/Diagnostic.h"17#include "clang/Basic/DiagnosticOptions.h"18#include "clang/Basic/SourceManager.h"19#include "clang/Basic/Version.h"20#include "clang/Format/Format.h"21#include "clang/Rewrite/Core/Rewriter.h"22#include "llvm/ADT/STLExtras.h"23#include "llvm/ADT/StringSet.h"24#include "llvm/Support/CommandLine.h"25 26using namespace llvm;27using namespace clang;28using namespace clang::replace;29 30static cl::opt<std::string> Directory(cl::Positional, cl::Required,31 cl::desc("<Search Root Directory>"));32 33static cl::OptionCategory ReplacementCategory("Replacement Options");34static cl::OptionCategory FormattingCategory("Formatting Options");35 36const cl::OptionCategory *VisibleCategories[] = {&ReplacementCategory,37 &FormattingCategory};38 39static cl::opt<bool> RemoveTUReplacementFiles(40 "remove-change-desc-files",41 cl::desc("Remove the change description files regardless of successful\n"42 "merging/replacing."),43 cl::init(false), cl::cat(ReplacementCategory));44 45static cl::opt<bool> IgnoreInsertConflict(46 "ignore-insert-conflict",47 cl::desc("Ignore insert conflict and keep running to fix."),48 cl::init(false), cl::cat(ReplacementCategory));49 50static cl::opt<bool> DoFormat(51 "format",52 cl::desc("Enable formatting of code changed by applying replacements.\n"53 "Use -style to choose formatting style.\n"),54 cl::cat(FormattingCategory));55 56// FIXME: Consider making the default behaviour for finding a style57// configuration file to start the search anew for every file being changed to58// handle situations where the style is different for different parts of a59// project.60 61static cl::opt<std::string> FormatStyleConfig(62 "style-config",63 cl::desc("Path to a directory containing a .clang-format file\n"64 "describing a formatting style to use for formatting\n"65 "code when -style=file.\n"),66 cl::init(""), cl::cat(FormattingCategory));67 68static cl::opt<std::string>69 FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),70 cl::init("LLVM"), cl::cat(FormattingCategory));71 72namespace {73// Helper object to remove the TUReplacement and TUDiagnostic (triggered by74// "remove-change-desc-files" command line option) when exiting current scope.75class ScopedFileRemover {76public:77 ScopedFileRemover(const TUReplacementFiles &Files,78 clang::DiagnosticsEngine &Diagnostics)79 : TURFiles(Files), Diag(Diagnostics) {}80 81 ~ScopedFileRemover() { deleteReplacementFiles(TURFiles, Diag); }82 83private:84 const TUReplacementFiles &TURFiles;85 clang::DiagnosticsEngine &Diag;86};87} // namespace88 89static void printVersion(raw_ostream &OS) {90 OS << "clang-apply-replacements version " CLANG_VERSION_STRING << "\n";91}92 93int main(int argc, char **argv) {94 cl::HideUnrelatedOptions(ArrayRef(VisibleCategories));95 96 cl::SetVersionPrinter(printVersion);97 cl::ParseCommandLineOptions(argc, argv);98 99 DiagnosticOptions DiagOpts;100 DiagnosticsEngine Diagnostics(DiagnosticIDs::create(), DiagOpts);101 102 // Determine a formatting style from options.103 auto FormatStyleOrError = format::getStyle(FormatStyleOpt, FormatStyleConfig,104 format::DefaultFallbackStyle);105 if (!FormatStyleOrError) {106 llvm::errs() << llvm::toString(FormatStyleOrError.takeError()) << "\n";107 return 1;108 }109 format::FormatStyle FormatStyle = std::move(*FormatStyleOrError);110 111 TUReplacements TURs;112 TUReplacementFiles TUFiles;113 114 std::error_code ErrorCode =115 collectReplacementsFromDirectory(Directory, TURs, TUFiles, Diagnostics);116 117 TUDiagnostics TUDs;118 TUFiles.clear();119 ErrorCode =120 collectReplacementsFromDirectory(Directory, TUDs, TUFiles, Diagnostics);121 122 if (ErrorCode) {123 errs() << "Trouble iterating over directory '" << Directory124 << "': " << ErrorCode.message() << "\n";125 return 1;126 }127 128 // Remove the TUReplacementFiles (triggered by "remove-change-desc-files"129 // command line option) when exiting main().130 std::unique_ptr<ScopedFileRemover> Remover;131 if (RemoveTUReplacementFiles)132 Remover.reset(new ScopedFileRemover(TUFiles, Diagnostics));133 134 FileManager Files((FileSystemOptions()));135 SourceManager SM(Diagnostics, Files);136 137 FileToChangesMap Changes;138 if (!mergeAndDeduplicate(TURs, TUDs, Changes, SM, IgnoreInsertConflict))139 return 1;140 141 tooling::ApplyChangesSpec Spec;142 Spec.Cleanup = true;143 Spec.Format = DoFormat ? tooling::ApplyChangesSpec::kAll144 : tooling::ApplyChangesSpec::kNone;145 Spec.Style = DoFormat ? FormatStyle : format::getNoStyle();146 147 for (const auto &FileChange : Changes) {148 FileEntryRef Entry = FileChange.first;149 StringRef FileName = Entry.getName();150 llvm::Expected<std::string> NewFileData =151 applyChanges(FileName, FileChange.second, Spec, Diagnostics);152 if (!NewFileData) {153 errs() << llvm::toString(NewFileData.takeError()) << "\n";154 continue;155 }156 157 // Write new file to disk158 std::error_code EC;159 llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::OF_None);160 if (EC) {161 llvm::errs() << "Could not open " << FileName << " for writing\n";162 continue;163 }164 FileStream << *NewFileData;165 }166 167 return 0;168}169