1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | #include "clang/Rewrite/Core/Rewriter.h" |
15 | #include "clang/Basic/Diagnostic.h" |
16 | #include "clang/Basic/DiagnosticIDs.h" |
17 | #include "clang/Basic/FileManager.h" |
18 | #include "clang/Basic/SourceLocation.h" |
19 | #include "clang/Basic/SourceManager.h" |
20 | #include "clang/Lex/Lexer.h" |
21 | #include "clang/Rewrite/Core/RewriteBuffer.h" |
22 | #include "clang/Rewrite/Core/RewriteRope.h" |
23 | #include "llvm/ADT/SmallString.h" |
24 | #include "llvm/ADT/SmallVector.h" |
25 | #include "llvm/ADT/StringRef.h" |
26 | #include "llvm/Support/FileSystem.h" |
27 | #include "llvm/Support/raw_ostream.h" |
28 | #include <cassert> |
29 | #include <iterator> |
30 | #include <map> |
31 | #include <memory> |
32 | #include <system_error> |
33 | #include <utility> |
34 | |
35 | using namespace clang; |
36 | |
37 | raw_ostream &RewriteBuffer::write(raw_ostream &os) const { |
38 | |
39 | |
40 | for (RopePieceBTreeIterator I = begin(), E = end(); I != E; |
41 | I.MoveToNextPiece()) |
42 | os << I.piece(); |
43 | return os; |
44 | } |
45 | |
46 | |
47 | |
48 | static inline bool isWhitespaceExceptNL(unsigned char c) { |
49 | switch (c) { |
50 | case ' ': |
51 | case '\t': |
52 | case '\f': |
53 | case '\v': |
54 | case '\r': |
55 | return true; |
56 | default: |
57 | return false; |
58 | } |
59 | } |
60 | |
61 | void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size, |
62 | bool removeLineIfEmpty) { |
63 | |
64 | if (Size == 0) return; |
65 | |
66 | unsigned RealOffset = getMappedOffset(OrigOffset, true); |
67 | (0) . __assert_fail ("RealOffset+Size <= Buffer.size() && \"Invalid location\"", "/home/seafit/code_projects/clang_source/clang/lib/Rewrite/Rewriter.cpp", 67, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(RealOffset+Size <= Buffer.size() && "Invalid location"); |
68 | |
69 | |
70 | Buffer.erase(RealOffset, Size); |
71 | |
72 | |
73 | AddReplaceDelta(OrigOffset, -Size); |
74 | |
75 | if (removeLineIfEmpty) { |
76 | |
77 | |
78 | |
79 | iterator curLineStart = begin(); |
80 | unsigned curLineStartOffs = 0; |
81 | iterator posI = begin(); |
82 | for (unsigned i = 0; i != RealOffset; ++i) { |
83 | if (*posI == '\n') { |
84 | curLineStart = posI; |
85 | ++curLineStart; |
86 | curLineStartOffs = i + 1; |
87 | } |
88 | ++posI; |
89 | } |
90 | |
91 | unsigned lineSize = 0; |
92 | posI = curLineStart; |
93 | while (posI != end() && isWhitespaceExceptNL(*posI)) { |
94 | ++posI; |
95 | ++lineSize; |
96 | } |
97 | if (posI != end() && *posI == '\n') { |
98 | Buffer.erase(curLineStartOffs, lineSize + 1); |
99 | AddReplaceDelta(curLineStartOffs, -(lineSize + 1)); |
100 | } |
101 | } |
102 | } |
103 | |
104 | void RewriteBuffer::InsertText(unsigned OrigOffset, StringRef Str, |
105 | bool InsertAfter) { |
106 | |
107 | if (Str.empty()) return; |
108 | |
109 | unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter); |
110 | Buffer.insert(RealOffset, Str.begin(), Str.end()); |
111 | |
112 | |
113 | AddInsertDelta(OrigOffset, Str.size()); |
114 | } |
115 | |
116 | |
117 | |
118 | |
119 | void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength, |
120 | StringRef NewStr) { |
121 | unsigned RealOffset = getMappedOffset(OrigOffset, true); |
122 | Buffer.erase(RealOffset, OrigLength); |
123 | Buffer.insert(RealOffset, NewStr.begin(), NewStr.end()); |
124 | if (OrigLength != NewStr.size()) |
125 | AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength); |
126 | } |
127 | |
128 | |
129 | |
130 | |
131 | |
132 | |
133 | |
134 | int Rewriter::getRangeSize(const CharSourceRange &Range, |
135 | RewriteOptions opts) const { |
136 | if (!isRewritable(Range.getBegin()) || |
137 | !isRewritable(Range.getEnd())) return -1; |
138 | |
139 | FileID StartFileID, EndFileID; |
140 | unsigned StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID); |
141 | unsigned EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID); |
142 | |
143 | if (StartFileID != EndFileID) |
144 | return -1; |
145 | |
146 | |
147 | |
148 | std::map<FileID, RewriteBuffer>::const_iterator I = |
149 | RewriteBuffers.find(StartFileID); |
150 | if (I != RewriteBuffers.end()) { |
151 | const RewriteBuffer &RB = I->second; |
152 | EndOff = RB.getMappedOffset(EndOff, opts.IncludeInsertsAtEndOfRange); |
153 | StartOff = RB.getMappedOffset(StartOff, !opts.IncludeInsertsAtBeginOfRange); |
154 | } |
155 | |
156 | |
157 | |
158 | if (Range.isTokenRange()) |
159 | EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); |
160 | |
161 | return EndOff-StartOff; |
162 | } |
163 | |
164 | int Rewriter::getRangeSize(SourceRange Range, RewriteOptions opts) const { |
165 | return getRangeSize(CharSourceRange::getTokenRange(Range), opts); |
166 | } |
167 | |
168 | |
169 | |
170 | |
171 | |
172 | |
173 | std::string Rewriter::getRewrittenText(SourceRange Range) const { |
174 | if (!isRewritable(Range.getBegin()) || |
175 | !isRewritable(Range.getEnd())) |
176 | return {}; |
177 | |
178 | FileID StartFileID, EndFileID; |
179 | unsigned StartOff, EndOff; |
180 | StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID); |
181 | EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID); |
182 | |
183 | if (StartFileID != EndFileID) |
184 | return {}; |
185 | |
186 | |
187 | |
188 | std::map<FileID, RewriteBuffer>::const_iterator I = |
189 | RewriteBuffers.find(StartFileID); |
190 | if (I == RewriteBuffers.end()) { |
191 | |
192 | const char *Ptr = SourceMgr->getCharacterData(Range.getBegin()); |
193 | |
194 | |
195 | |
196 | EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); |
197 | return std::string(Ptr, Ptr+EndOff-StartOff); |
198 | } |
199 | |
200 | const RewriteBuffer &RB = I->second; |
201 | EndOff = RB.getMappedOffset(EndOff, true); |
202 | StartOff = RB.getMappedOffset(StartOff); |
203 | |
204 | |
205 | |
206 | EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); |
207 | |
208 | |
209 | RewriteBuffer::iterator Start = RB.begin(); |
210 | std::advance(Start, StartOff); |
211 | RewriteBuffer::iterator End = Start; |
212 | std::advance(End, EndOff-StartOff); |
213 | |
214 | return std::string(Start, End); |
215 | } |
216 | |
217 | unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc, |
218 | FileID &FID) const { |
219 | (0) . __assert_fail ("Loc.isValid() && \"Invalid location\"", "/home/seafit/code_projects/clang_source/clang/lib/Rewrite/Rewriter.cpp", 219, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(Loc.isValid() && "Invalid location"); |
220 | std::pair<FileID, unsigned> V = SourceMgr->getDecomposedLoc(Loc); |
221 | FID = V.first; |
222 | return V.second; |
223 | } |
224 | |
225 | |
226 | RewriteBuffer &Rewriter::getEditBuffer(FileID FID) { |
227 | std::map<FileID, RewriteBuffer>::iterator I = |
228 | RewriteBuffers.lower_bound(FID); |
229 | if (I != RewriteBuffers.end() && I->first == FID) |
230 | return I->second; |
231 | I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer())); |
232 | |
233 | StringRef MB = SourceMgr->getBufferData(FID); |
234 | I->second.Initialize(MB.begin(), MB.end()); |
235 | |
236 | return I->second; |
237 | } |
238 | |
239 | |
240 | |
241 | bool Rewriter::InsertText(SourceLocation Loc, StringRef Str, |
242 | bool InsertAfter, bool indentNewLines) { |
243 | if (!isRewritable(Loc)) return true; |
244 | FileID FID; |
245 | unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID); |
246 | |
247 | SmallString<128> indentedStr; |
248 | if (indentNewLines && Str.find('\n') != StringRef::npos) { |
249 | StringRef MB = SourceMgr->getBufferData(FID); |
250 | |
251 | unsigned lineNo = SourceMgr->getLineNumber(FID, StartOffs) - 1; |
252 | const SrcMgr::ContentCache * |
253 | Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache(); |
254 | unsigned lineOffs = Content->SourceLineCache[lineNo]; |
255 | |
256 | |
257 | StringRef indentSpace; |
258 | { |
259 | unsigned i = lineOffs; |
260 | while (isWhitespaceExceptNL(MB[i])) |
261 | ++i; |
262 | indentSpace = MB.substr(lineOffs, i-lineOffs); |
263 | } |
264 | |
265 | SmallVector<StringRef, 4> lines; |
266 | Str.split(lines, "\n"); |
267 | |
268 | for (unsigned i = 0, e = lines.size(); i != e; ++i) { |
269 | indentedStr += lines[i]; |
270 | if (i < e-1) { |
271 | indentedStr += '\n'; |
272 | indentedStr += indentSpace; |
273 | } |
274 | } |
275 | Str = indentedStr.str(); |
276 | } |
277 | |
278 | getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter); |
279 | return false; |
280 | } |
281 | |
282 | bool Rewriter::InsertTextAfterToken(SourceLocation Loc, StringRef Str) { |
283 | if (!isRewritable(Loc)) return true; |
284 | FileID FID; |
285 | unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID); |
286 | RewriteOptions rangeOpts; |
287 | rangeOpts.IncludeInsertsAtBeginOfRange = false; |
288 | StartOffs += getRangeSize(SourceRange(Loc, Loc), rangeOpts); |
289 | getEditBuffer(FID).InsertText(StartOffs, Str, ); |
290 | return false; |
291 | } |
292 | |
293 | |
294 | bool Rewriter::RemoveText(SourceLocation Start, unsigned Length, |
295 | RewriteOptions opts) { |
296 | if (!isRewritable(Start)) return true; |
297 | FileID FID; |
298 | unsigned StartOffs = getLocationOffsetAndFileID(Start, FID); |
299 | getEditBuffer(FID).RemoveText(StartOffs, Length, opts.RemoveLineIfEmpty); |
300 | return false; |
301 | } |
302 | |
303 | |
304 | |
305 | |
306 | bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength, |
307 | StringRef NewStr) { |
308 | if (!isRewritable(Start)) return true; |
309 | FileID StartFileID; |
310 | unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID); |
311 | |
312 | getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, NewStr); |
313 | return false; |
314 | } |
315 | |
316 | bool Rewriter::ReplaceText(SourceRange range, SourceRange replacementRange) { |
317 | if (!isRewritable(range.getBegin())) return true; |
318 | if (!isRewritable(range.getEnd())) return true; |
319 | if (replacementRange.isInvalid()) return true; |
320 | SourceLocation start = range.getBegin(); |
321 | unsigned origLength = getRangeSize(range); |
322 | unsigned newLength = getRangeSize(replacementRange); |
323 | FileID FID; |
324 | unsigned newOffs = getLocationOffsetAndFileID(replacementRange.getBegin(), |
325 | FID); |
326 | StringRef MB = SourceMgr->getBufferData(FID); |
327 | return ReplaceText(start, origLength, MB.substr(newOffs, newLength)); |
328 | } |
329 | |
330 | bool Rewriter::IncreaseIndentation(CharSourceRange range, |
331 | SourceLocation parentIndent) { |
332 | if (range.isInvalid()) return true; |
333 | if (!isRewritable(range.getBegin())) return true; |
334 | if (!isRewritable(range.getEnd())) return true; |
335 | if (!isRewritable(parentIndent)) return true; |
336 | |
337 | FileID StartFileID, EndFileID, parentFileID; |
338 | unsigned StartOff, EndOff, parentOff; |
339 | |
340 | StartOff = getLocationOffsetAndFileID(range.getBegin(), StartFileID); |
341 | EndOff = getLocationOffsetAndFileID(range.getEnd(), EndFileID); |
342 | parentOff = getLocationOffsetAndFileID(parentIndent, parentFileID); |
343 | |
344 | if (StartFileID != EndFileID || StartFileID != parentFileID) |
345 | return true; |
346 | if (StartOff > EndOff) |
347 | return true; |
348 | |
349 | FileID FID = StartFileID; |
350 | StringRef MB = SourceMgr->getBufferData(FID); |
351 | |
352 | unsigned parentLineNo = SourceMgr->getLineNumber(FID, parentOff) - 1; |
353 | unsigned startLineNo = SourceMgr->getLineNumber(FID, StartOff) - 1; |
354 | unsigned endLineNo = SourceMgr->getLineNumber(FID, EndOff) - 1; |
355 | |
356 | const SrcMgr::ContentCache * |
357 | Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache(); |
358 | |
359 | |
360 | unsigned parentLineOffs = Content->SourceLineCache[parentLineNo]; |
361 | unsigned startLineOffs = Content->SourceLineCache[startLineNo]; |
362 | |
363 | |
364 | StringRef parentSpace, startSpace; |
365 | { |
366 | unsigned i = parentLineOffs; |
367 | while (isWhitespaceExceptNL(MB[i])) |
368 | ++i; |
369 | parentSpace = MB.substr(parentLineOffs, i-parentLineOffs); |
370 | |
371 | i = startLineOffs; |
372 | while (isWhitespaceExceptNL(MB[i])) |
373 | ++i; |
374 | startSpace = MB.substr(startLineOffs, i-startLineOffs); |
375 | } |
376 | if (parentSpace.size() >= startSpace.size()) |
377 | return true; |
378 | if (!startSpace.startswith(parentSpace)) |
379 | return true; |
380 | |
381 | StringRef indent = startSpace.substr(parentSpace.size()); |
382 | |
383 | |
384 | RewriteBuffer &RB = getEditBuffer(FID); |
385 | for (unsigned lineNo = startLineNo; lineNo <= endLineNo; ++lineNo) { |
386 | unsigned offs = Content->SourceLineCache[lineNo]; |
387 | unsigned i = offs; |
388 | while (isWhitespaceExceptNL(MB[i])) |
389 | ++i; |
390 | StringRef origIndent = MB.substr(offs, i-offs); |
391 | if (origIndent.startswith(startSpace)) |
392 | RB.InsertText(offs, indent, ); |
393 | } |
394 | |
395 | return false; |
396 | } |
397 | |
398 | namespace { |
399 | |
400 | |
401 | |
402 | |
403 | |
404 | |
405 | |
406 | class AtomicallyMovedFile { |
407 | public: |
408 | AtomicallyMovedFile(DiagnosticsEngine &Diagnostics, StringRef Filename, |
409 | bool &AllWritten) |
410 | : Diagnostics(Diagnostics), Filename(Filename), AllWritten(AllWritten) { |
411 | TempFilename = Filename; |
412 | TempFilename += "-%%%%%%%%"; |
413 | int FD; |
414 | if (llvm::sys::fs::createUniqueFile(TempFilename, FD, TempFilename)) { |
415 | AllWritten = false; |
416 | Diagnostics.Report(clang::diag::err_unable_to_make_temp) |
417 | << TempFilename; |
418 | } else { |
419 | FileStream.reset(new llvm::raw_fd_ostream(FD, )); |
420 | } |
421 | } |
422 | |
423 | ~AtomicallyMovedFile() { |
424 | if (!ok()) return; |
425 | |
426 | |
427 | FileStream->close(); |
428 | if (std::error_code ec = llvm::sys::fs::rename(TempFilename, Filename)) { |
429 | AllWritten = false; |
430 | Diagnostics.Report(clang::diag::err_unable_to_rename_temp) |
431 | << TempFilename << Filename << ec.message(); |
432 | |
433 | |
434 | llvm::sys::fs::remove(TempFilename); |
435 | } |
436 | } |
437 | |
438 | bool ok() { return (bool)FileStream; } |
439 | raw_ostream &getStream() { return *FileStream; } |
440 | |
441 | private: |
442 | DiagnosticsEngine &Diagnostics; |
443 | StringRef Filename; |
444 | SmallString<128> TempFilename; |
445 | std::unique_ptr<llvm::raw_fd_ostream> FileStream; |
446 | bool &AllWritten; |
447 | }; |
448 | |
449 | } |
450 | |
451 | bool Rewriter::overwriteChangedFiles() { |
452 | bool AllWritten = true; |
453 | for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { |
454 | const FileEntry *Entry = |
455 | getSourceMgr().getFileEntryForID(I->first); |
456 | AtomicallyMovedFile File(getSourceMgr().getDiagnostics(), Entry->getName(), |
457 | AllWritten); |
458 | if (File.ok()) { |
459 | I->second.write(File.getStream()); |
460 | } |
461 | } |
462 | return !AllWritten; |
463 | } |
464 | |