18 #include "clang/Basic/LangOptions.h" 19 #include "clang/Basic/SourceManager.h" 20 #include "clang/Format/Format.h" 21 #include "clang/Lex/Lexer.h" 22 #include "clang/Rewrite/Core/Rewriter.h" 23 #include "clang/Tooling/DiagnosticsYaml.h" 24 #include "clang/Tooling/ReplacementsYaml.h" 25 #include "llvm/ADT/ArrayRef.h" 26 #include "llvm/Support/FileSystem.h" 27 #include "llvm/Support/MemoryBuffer.h" 28 #include "llvm/Support/Path.h" 29 #include "llvm/Support/raw_ostream.h" 32 using namespace clang;
42 using namespace llvm::sys::fs;
43 using namespace llvm::sys::path;
45 std::error_code ErrorCode;
47 for (recursive_directory_iterator I(Directory, ErrorCode), E;
48 I != E && !ErrorCode; I.increment(ErrorCode)) {
49 if (filename(I->path())[0] ==
'.') {
55 if (extension(I->path()) !=
".yaml")
58 TUFiles.push_back(I->path());
60 ErrorOr<std::unique_ptr<MemoryBuffer>> Out =
61 MemoryBuffer::getFile(I->path());
62 if (std::error_code BufferError = Out.getError()) {
63 errs() <<
"Error reading " << I->path() <<
": " << BufferError.message()
69 tooling::TranslationUnitReplacements TU;
86 clang::DiagnosticsEngine &Diagnostics) {
87 using namespace llvm::sys::fs;
88 using namespace llvm::sys::path;
90 std::error_code ErrorCode;
92 for (recursive_directory_iterator I(Directory, ErrorCode), E;
93 I != E && !ErrorCode; I.increment(ErrorCode)) {
94 if (filename(I->path())[0] ==
'.') {
100 if (extension(I->path()) !=
".yaml")
103 TUFiles.push_back(I->path());
105 ErrorOr<std::unique_ptr<MemoryBuffer>> Out =
106 MemoryBuffer::getFile(I->path());
107 if (std::error_code BufferError = Out.getError()) {
108 errs() <<
"Error reading " << I->path() <<
": " << BufferError.message()
113 yaml::Input YIn(Out.get()->getBuffer(),
nullptr, &
eatDiagnostics);
114 tooling::TranslationUnitDiagnostics TU;
135 const FileEntry *
File,
136 const llvm::ArrayRef<clang::tooling::Replacement> ConflictingReplacements,
138 FileID FID = SM.translateFile(File);
140 FID = SM.createFileID(File, SourceLocation(), SrcMgr::C_User);
143 errs() <<
"The following changes conflict:\n";
144 for (
const tooling::Replacement &R : ConflictingReplacements) {
145 if (R.getLength() == 0) {
146 errs() <<
" Insert at " << SM.getLineNumber(FID, R.getOffset()) <<
":" 147 << SM.getColumnNumber(FID, R.getOffset()) <<
" " 148 << R.getReplacementText() <<
"\n";
150 if (R.getReplacementText().empty())
151 errs() <<
" Remove ";
153 errs() <<
" Replace ";
155 errs() << SM.getLineNumber(FID, R.getOffset()) <<
":" 156 << SM.getColumnNumber(FID, R.getOffset()) <<
"-" 157 << SM.getLineNumber(FID, R.getOffset() + R.getLength() - 1) <<
":" 158 << SM.getColumnNumber(FID, R.getOffset() + R.getLength() - 1);
160 if (R.getReplacementText().empty())
163 errs() <<
" with \"" << R.getReplacementText() <<
"\"\n";
173 for (
auto I = Replaces.begin(), E = Replaces.end(); I != E; ++I) {
174 if (I->isApplicable()) {
175 Result = I->apply(Rewrite) && Result;
185 static void deduplicate(std::vector<tooling::Replacement> &Replaces,
186 std::vector<tooling::Range> &Conflicts) {
187 if (Replaces.empty())
190 auto LessNoPath = [](
const tooling::Replacement &LHS,
191 const tooling::Replacement &RHS) {
192 if (LHS.getOffset() != RHS.getOffset())
193 return LHS.getOffset() < RHS.getOffset();
194 if (LHS.getLength() != RHS.getLength())
195 return LHS.getLength() < RHS.getLength();
196 return LHS.getReplacementText() < RHS.getReplacementText();
199 auto EqualNoPath = [](
const tooling::Replacement &LHS,
200 const tooling::Replacement &RHS) {
201 return LHS.getOffset() == RHS.getOffset() &&
202 LHS.getLength() == RHS.getLength() &&
203 LHS.getReplacementText() == RHS.getReplacementText();
208 std::sort(Replaces.begin(), Replaces.end(), LessNoPath);
209 Replaces.erase(std::unique(Replaces.begin(), Replaces.end(), EqualNoPath),
214 Replaces.front().getLength());
215 unsigned ConflictStart = 0;
216 unsigned ConflictLength = 1;
217 for (
unsigned i = 1; i < Replaces.size(); ++i) {
218 tooling::Range Current(Replaces[i].getOffset(), Replaces[i].getLength());
219 if (ConflictRange.overlapsWith(Current)) {
223 std::max(ConflictRange.getLength(),
224 Current.getOffset() + Current.getLength() -
225 ConflictRange.getOffset()));
228 if (ConflictLength > 1)
229 Conflicts.push_back(
tooling::Range(ConflictStart, ConflictLength));
230 ConflictRange = Current;
236 if (ConflictLength > 1)
237 Conflicts.push_back(
tooling::Range(ConflictStart, ConflictLength));
254 bool conflictsFound =
false;
256 for (
auto &FileAndReplacements : Replacements) {
257 const FileEntry *
Entry = FileAndReplacements.first;
258 auto &Replacements = FileAndReplacements.second;
259 assert(Entry !=
nullptr &&
"No file entry!");
261 std::vector<tooling::Range> Conflicts;
262 deduplicate(FileAndReplacements.second, Conflicts);
264 if (Conflicts.empty())
267 conflictsFound =
true;
269 errs() <<
"There are conflicting changes to " << Entry->getName() <<
":\n";
272 auto ConflictingReplacements = llvm::makeArrayRef(
273 &Replacements[Conflict.getOffset()], Conflict.getLength());
278 return conflictsFound;
283 clang::SourceManager &SM) {
286 std::set<StringRef> Warned;
287 for (
const auto &TU : TUs) {
288 for (
const tooling::Replacement &R : TU.Replacements) {
291 if (
const FileEntry *
Entry = SM.getFileManager().getFile(R.getFilePath())) {
292 GroupedReplacements[
Entry].push_back(R);
293 }
else if (Warned.insert(R.getFilePath()).second) {
294 errs() <<
"Described file '" << R.getFilePath()
295 <<
"' doesn't exist. Ignoring...\n";
306 clang::SourceManager &SM) {
309 std::set<StringRef> Warned;
310 for (
const auto &TU : TUs) {
311 for (
const auto &D : TU.Diagnostics) {
312 for (
const auto &
Fix : D.Fix) {
313 for (
const tooling::Replacement &R :
Fix.second) {
316 if (
const FileEntry *
Entry = SM.getFileManager().getFile(R.getFilePath())) {
317 GroupedReplacements[
Entry].push_back(R);
318 }
else if (Warned.insert(R.getFilePath()).second) {
319 errs() <<
"Described file '" << R.getFilePath()
320 <<
"' doesn't exist. Ignoring...\n";
332 clang::Rewriter &Rewrites) {
340 for (
const auto &FileAndReplacements : GroupedReplacements) {
349 const std::vector<clang::tooling::Replacement> &Replaces) {
354 for (
const tooling::Replacement &R : Replaces) {
355 unsigned Offset = R.getOffset() + Shift;
356 unsigned Length = R.getReplacementText().size();
357 Shift += Length - R.getLength();
361 return ChangedRanges;
366 for (
auto BufferI = Rewrites.buffer_begin(), BufferE = Rewrites.buffer_end();
367 BufferI != BufferE; ++BufferI) {
369 Rewrites.getSourceMgr().getFileEntryForID(BufferI->first)->getName();
372 llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_Text);
374 errs() <<
"Warning: Could not write to " << EC.message() <<
"\n";
377 BufferI->second.write(FileStream);
384 clang::DiagnosticsEngine &Diagnostics) {
386 for (
const auto &
Filename : Files) {
387 std::error_code Error = llvm::sys::fs::remove(
Filename);
391 errs() <<
"Error deleting file: " <<
Filename <<
"\n";
392 errs() << Error.message() <<
"\n";
393 errs() <<
"Please delete the file manually\n";
static bool deduplicateAndDetectConflicts(FileToReplacementsMap &Replacements, SourceManager &SM)
Deduplicates and tests for conflicts among the replacements for each file in Replacements.
static void reportConflict(const FileEntry *File, const llvm::ArrayRef< clang::tooling::Replacement > ConflictingReplacements, SourceManager &SM)
Dumps information for a sequence of conflicting Replacements.
llvm::DenseMap< const clang::FileEntry *, std::vector< clang::tooling::Replacement > > FileToReplacementsMap
Map mapping file name to Replacements targeting that file.
bool deleteReplacementFiles(const TUReplacementFiles &Files, clang::DiagnosticsEngine &Diagnostics)
Delete the replacement files.
std::vector< clang::tooling::TranslationUnitReplacements > TUReplacements
Collection of TranslationUnitReplacements.
static void eatDiagnostics(const SMDiagnostic &, void *)
bool writeFiles(const clang::Rewriter &Rewrites)
Write the contents of FileContents to disk.
static cl::opt< std::string > Directory(cl::Positional, cl::Required, cl::desc("<Search Root Directory>"))
std::string Filename
Filename as a string.
std::vector< clang::tooling::Range > RangeVector
Collection of source ranges.
static void deduplicate(std::vector< tooling::Replacement > &Replaces, std::vector< tooling::Range > &Conflicts)
RangeVector calculateChangedRanges(const std::vector< clang::tooling::Replacement > &Replacements)
Given a collection of Replacements for a single file, produces a list of source ranges that enclose t...
std::error_code collectReplacementsFromDirectory(const llvm::StringRef Directory, TUReplacements &TUs, TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics)
Recursively descends through a directory structure rooted at Directory and attempts to deserialize *...
CharSourceRange Range
SourceRange for the file name.
This file provides the interface for deduplicating, detecting conflicts in, and applying collections ...
std::vector< clang::tooling::TranslationUnitDiagnostics > TUDiagnostics
Collection of TranslationUniDiagnostics.
std::vector< std::string > TUReplacementFiles
Collection of TranslationUnitReplacement files.
static cl::opt< bool > Fix("fix", cl::desc(R"(
Apply suggested fixes. Without -fix-errors
clang-tidy will bail out if any compilation
errors were found.
)"), cl::init(false), cl::cat(ClangTidyCategory))
bool applyAllReplacements(const std::vector< tooling::Replacement > &Replaces, Rewriter &Rewrite)
bool mergeAndDeduplicate(const TUReplacements &TUs, FileToReplacementsMap &GroupedReplacements, clang::SourceManager &SM)
Deduplicate, check for conflicts, and apply all Replacements stored in TUs.
static bool applyReplacements(const std::vector< tooling::Replacement > &Replacements, std::string &Result, DiagnosticsEngine &Diagnostics)
Apply Replacements and return the new file contents.