clang-tools 3.8.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

ApplyReplacements.cpp
Go to the documentation of this file.
1 //===-- ApplyReplacements.cpp - Apply and deduplicate replacements --------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// \brief This file provides the implementation for deduplicating, detecting
12 /// conflicts in, and applying collections of Replacements.
13 ///
14 /// FIXME: Use Diagnostics for output instead of llvm::errs().
15 ///
16 //===----------------------------------------------------------------------===//
17 #include "clang-apply-replacements/Tooling/ApplyReplacements.h"
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/ReplacementsYaml.h"
24 #include "llvm/ADT/ArrayRef.h"
25 #include "llvm/Support/FileSystem.h"
26 #include "llvm/Support/MemoryBuffer.h"
27 #include "llvm/Support/Path.h"
28 #include "llvm/Support/raw_ostream.h"
29
30 using namespace llvm;
31 using namespace clang;
32
33
34 static void eatDiagnostics(const SMDiagnostic &, void *) {}
35
36 namespace clang {
37 namespace replace {
38
39 std::error_code
40 collectReplacementsFromDirectory(const llvm::StringRef Directory,
41 TUReplacements &TUs,
42 TUReplacementFiles & TURFiles,
43 clang::DiagnosticsEngine &Diagnostics) {
44 using namespace llvm::sys::fs;
45 using namespace llvm::sys::path;
46
47 std::error_code ErrorCode;
48
49 for (recursive_directory_iterator I(Directory, ErrorCode), E;
50 I != E && !ErrorCode; I.increment(ErrorCode)) {
51 if (filename(I->path())[0] == '.') {
52 // Indicate not to descend into directories beginning with '.'
53 I.no_push();
54 continue;
55 }
56
57 if (extension(I->path()) != ".yaml")
58 continue;
59
60 TURFiles.push_back(I->path());
61
62 ErrorOr<std::unique_ptr<MemoryBuffer>> Out =
63 MemoryBuffer::getFile(I->path());
64 if (std::error_code BufferError = Out.getError()) {
65 errs() << "Error reading " << I->path() << ": " << BufferError.message()
66 << "\n";
67 continue;
68 }
69
70 yaml::Input YIn(Out.get()->getBuffer(), nullptr, &eatDiagnostics);
71 tooling::TranslationUnitReplacements TU;
72 YIn >> TU;
73 if (YIn.error()) {
74 // File doesn't appear to be a header change description. Ignore it.
75 continue;
76 }
77
78 // Only keep files that properly parse.
79 TUs.push_back(TU);
80 }
81
82 return ErrorCode;
83 }
84
85 /// \brief Dumps information for a sequence of conflicting Replacements.
86 ///
87 /// \param[in] File FileEntry for the file the conflicting Replacements are
88 /// for.
89 /// \param[in] ConflictingReplacements List of conflicting Replacements.
90 /// \param[in] SM SourceManager used for reporting.
91 static void reportConflict(
92 const FileEntry *File,
93 const llvm::ArrayRef<clang::tooling::Replacement> ConflictingReplacements,
94 SourceManager &SM) {
95 FileID FID = SM.translateFile(File);
96 if (FID.isInvalid())
97 FID = SM.createFileID(File, SourceLocation(), SrcMgr::C_User);
98
99 // FIXME: Output something a little more user-friendly (e.g. unified diff?)
100 errs() << "The following changes conflict:\n";
101 for (const tooling::Replacement &R : ConflictingReplacements) {
102 if (R.getLength() == 0) {
103 errs() << " Insert at " << SM.getLineNumber(FID, R.getOffset()) << ":"
104 << SM.getColumnNumber(FID, R.getOffset()) << " "
105 << R.getReplacementText() << "\n";
106 } else {
107 if (R.getReplacementText().empty())
108 errs() << " Remove ";
109 else
110 errs() << " Replace ";
111
112 errs() << SM.getLineNumber(FID, R.getOffset()) << ":"
113 << SM.getColumnNumber(FID, R.getOffset()) << "-"
114 << SM.getLineNumber(FID, R.getOffset() + R.getLength() - 1) << ":"
115 << SM.getColumnNumber(FID, R.getOffset() + R.getLength() - 1);
116
117 if (R.getReplacementText().empty())
118 errs() << "\n";
119 else
120 errs() << " with \"" << R.getReplacementText() << "\"\n";
121 }
122 }
123 }
124
125 /// \brief Deduplicates and tests for conflicts among the replacements for each
126 /// file in \c Replacements. Any conflicts found are reported.
127 ///
128 /// \post Replacements[i].getOffset() <= Replacements[i+1].getOffset().
129 ///
130 /// \param[in,out] Replacements Container of all replacements grouped by file
131 /// to be deduplicated and checked for conflicts.
132 /// \param[in] SM SourceManager required for conflict reporting.
133 ///
134 /// \returns \parblock
135 /// \li true if conflicts were detected
136 /// \li false if no conflicts were detected
137 static bool deduplicateAndDetectConflicts(FileToReplacementsMap &Replacements,
138 SourceManager &SM) {
139 bool conflictsFound = false;
140
141 for (auto &FileAndReplacements : Replacements) {
142 const FileEntry *Entry = FileAndReplacements.first;
143 auto &Replacements = FileAndReplacements.second;
144 assert(Entry != nullptr && "No file entry!");
145
146 std::vector<tooling::Range> Conflicts;
147 tooling::deduplicate(FileAndReplacements.second, Conflicts);
148
149 if (Conflicts.empty())
150 continue;
151
152 conflictsFound = true;
153
154 errs() << "There are conflicting changes to " << Entry->getName() << ":\n";
155
156 for (const tooling::Range &Conflict : Conflicts) {
157 auto ConflictingReplacements = llvm::makeArrayRef(
158 &Replacements[Conflict.getOffset()], Conflict.getLength());
159 reportConflict(Entry, ConflictingReplacements, SM);
160 }
161 }
162
163 return conflictsFound;
164 }
165
166 bool mergeAndDeduplicate(const TUReplacements &TUs,
167 FileToReplacementsMap &GroupedReplacements,
168 clang::SourceManager &SM) {
169
170 // Group all replacements by target file.
171 std::set<StringRef> Warned;
172 for (const auto &TU : TUs) {
173 for (const tooling::Replacement &R : TU.Replacements) {
174 // Use the file manager to deduplicate paths. FileEntries are
175 // automatically canonicalized.
176 const FileEntry *Entry = SM.getFileManager().getFile(R.getFilePath());
177 if (!Entry && Warned.insert(R.getFilePath()).second) {
178 errs() << "Described file '" << R.getFilePath()
179 << "' doesn't exist. Ignoring...\n";
180 continue;
181 }
182 GroupedReplacements[Entry].push_back(R);
183 }
184 }
185
186 // Ask clang to deduplicate and report conflicts.
187 return !deduplicateAndDetectConflicts(GroupedReplacements, SM);
188 }
189
190 bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
191 clang::Rewriter &Rewrites) {
192
193 // Apply all changes
194 //
195 // FIXME: No longer certain GroupedReplacements is really the best kind of
196 // data structure for applying replacements. Rewriter certainly doesn't care.
197 // However, until we nail down the design of ReplacementGroups, might as well
198 // leave this as is.
199 for (const auto &FileAndReplacements : GroupedReplacements) {
200 if (!tooling::applyAllReplacements(FileAndReplacements.second, Rewrites))
201 return false;
202 }
203
204 return true;
205 }
206
207 RangeVector calculateChangedRanges(
208 const std::vector<clang::tooling::Replacement> &Replaces) {
209 RangeVector ChangedRanges;
210
211 // Generate the new ranges from the replacements.
212 int Shift = 0;
213 for (const tooling::Replacement &R : Replaces) {
214 unsigned Offset = R.getOffset() + Shift;
215 unsigned Length = R.getReplacementText().size();
216 Shift += Length - R.getLength();
217 ChangedRanges.push_back(tooling::Range(Offset, Length));
218 }
219
220 return ChangedRanges;
221 }
222
223 bool writeFiles(const clang::Rewriter &Rewrites) {
224
225 for (Rewriter::const_buffer_iterator BufferI = Rewrites.buffer_begin(),
226 BufferE = Rewrites.buffer_end();
227 BufferI != BufferE; ++BufferI) {
228 const char *FileName =
229 Rewrites.getSourceMgr().getFileEntryForID(BufferI->first)->getName();
230
231 std::error_code EC;
232 llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_Text);
233 if (EC) {
234 errs() << "Warning: Could not write to " << EC.message() << "\n";
235 continue;
236 }
237 BufferI->second.write(FileStream);
238 }
239
240 return true;
241 }
242
243 bool deleteReplacementFiles(const TUReplacementFiles &Files,
244 clang::DiagnosticsEngine &Diagnostics) {
245 bool Success = true;
246 for (const auto &Filename : Files) {
247 std::error_code Error = llvm::sys::fs::remove(Filename);
248 if (Error) {
249 Success = false;
250 // FIXME: Use Diagnostics for outputting errors.
251 errs() << "Error deleting file: " << Filename << "\n";
252 errs() << Error.message() << "\n";
253 errs() << "Please delete the file manually\n";
254 }
255 }
256 return Success;
257 }
258
259 } // end namespace replace
260 } // end namespace clang
clang::replace::deduplicateAndDetectConflicts
static bool deduplicateAndDetectConflicts(FileToReplacementsMap &Replacements, SourceManager &SM)
Deduplicates and tests for conflicts among the replacements for each file in Replacements.
Definition: ApplyReplacements.cpp:137
clang::replace::reportConflict
static void reportConflict(const FileEntry *File, const llvm::ArrayRef< clang::tooling::Replacement > ConflictingReplacements, SourceManager &SM)
Dumps information for a sequence of conflicting Replacements.
Definition: ApplyReplacements.cpp:91
clang::replace::FileToReplacementsMap
llvm::DenseMap< const clang::FileEntry *, std::vector< clang::tooling::Replacement > > FileToReplacementsMap
Map mapping file name to Replacements targeting that file.
Definition: ApplyReplacements.h:50
clang::replace::deleteReplacementFiles
bool deleteReplacementFiles(const TUReplacementFiles &Files, clang::DiagnosticsEngine &Diagnostics)
Delete the replacement files.
Definition: ApplyReplacements.cpp:243
File
HeaderHandle File
Definition: PreprocessorTracker.cpp:526
clang::replace::TUReplacements
std::vector< clang::tooling::TranslationUnitReplacements > TUReplacements
Collection of TranslationUnitReplacements.
Definition: ApplyReplacements.h:42
eatDiagnostics
static void eatDiagnostics(const SMDiagnostic &, void *)
Definition: ApplyReplacements.cpp:34
clang::replace::collectReplacementsFromDirectory
std::error_code collectReplacementsFromDirectory(const llvm::StringRef Directory, TUReplacements &TUs, TUReplacementFiles &TURFiles, clang::DiagnosticsEngine &Diagnostics)
Recursively descends through a directory structure rooted at Directory and attempts to deserialize *...
Definition: ApplyReplacements.cpp:40
clang::replace::writeFiles
bool writeFiles(const clang::Rewriter &Rewrites)
Write the contents of FileContents to disk.
Definition: ApplyReplacements.cpp:223
Directory
static cl::opt< std::string > Directory(cl::Positional, cl::Required, cl::desc("<Search Root Directory>"))
SM
SourceManager & SM
Definition: IncludeOrderCheck.cpp:44
Filename
std::string Filename
Filename as a string.
Definition: IncludeOrderCheck.cpp:36
clang::replace::RangeVector
std::vector< clang::tooling::Range > RangeVector
Collection of source ranges.
Definition: ApplyReplacements.h:38
clang::replace::calculateChangedRanges
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...
Definition: ApplyReplacements.cpp:207
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:35
Files
FileManager Files
Definition: ClangTidy.cpp:167
ApplyReplacements.h
This file provides the interface for deduplicating, detecting conflicts in, and applying collections ...
clang::replace::TUReplacementFiles
std::vector< std::string > TUReplacementFiles
Collection of TranslationUnitReplacement files.
Definition: ApplyReplacements.h:45
clang::replace::mergeAndDeduplicate
bool mergeAndDeduplicate(const TUReplacements &TUs, FileToReplacementsMap &GroupedReplacements, clang::SourceManager &SM)
Deduplicate, check for conflicts, and apply all Replacements stored in TUs.
Definition: ApplyReplacements.cpp:166
Entry
Definition: Modularize.cpp:432
applyReplacements
static bool applyReplacements(const std::vector< tooling::Replacement > &Replacements, std::string &Result, DiagnosticsEngine &Diagnostics)
Apply Replacements and return the new file contents.
Definition: ClangApplyReplacementsMain.cpp:142