xref: /openbsd-src/gnu/llvm/clang/lib/Rewrite/Rewriter.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1e5dd7070Spatrick //===- Rewriter.cpp - Code rewriting interface ----------------------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick //  This file defines the Rewriter class, which is used for code
10e5dd7070Spatrick //  transformations.
11e5dd7070Spatrick //
12e5dd7070Spatrick //===----------------------------------------------------------------------===//
13e5dd7070Spatrick 
14e5dd7070Spatrick #include "clang/Rewrite/Core/Rewriter.h"
15e5dd7070Spatrick #include "clang/Basic/Diagnostic.h"
16e5dd7070Spatrick #include "clang/Basic/DiagnosticIDs.h"
17e5dd7070Spatrick #include "clang/Basic/FileManager.h"
18e5dd7070Spatrick #include "clang/Basic/SourceLocation.h"
19e5dd7070Spatrick #include "clang/Basic/SourceManager.h"
20e5dd7070Spatrick #include "clang/Lex/Lexer.h"
21e5dd7070Spatrick #include "clang/Rewrite/Core/RewriteBuffer.h"
22e5dd7070Spatrick #include "clang/Rewrite/Core/RewriteRope.h"
23e5dd7070Spatrick #include "llvm/ADT/SmallString.h"
24e5dd7070Spatrick #include "llvm/ADT/SmallVector.h"
25e5dd7070Spatrick #include "llvm/ADT/StringRef.h"
26e5dd7070Spatrick #include "llvm/Support/FileSystem.h"
27e5dd7070Spatrick #include "llvm/Support/raw_ostream.h"
28e5dd7070Spatrick #include <cassert>
29e5dd7070Spatrick #include <iterator>
30e5dd7070Spatrick #include <map>
31e5dd7070Spatrick #include <memory>
32e5dd7070Spatrick #include <system_error>
33e5dd7070Spatrick #include <utility>
34e5dd7070Spatrick 
35e5dd7070Spatrick using namespace clang;
36e5dd7070Spatrick 
write(raw_ostream & os) const37e5dd7070Spatrick raw_ostream &RewriteBuffer::write(raw_ostream &os) const {
38e5dd7070Spatrick   // Walk RewriteRope chunks efficiently using MoveToNextPiece() instead of the
39e5dd7070Spatrick   // character iterator.
40e5dd7070Spatrick   for (RopePieceBTreeIterator I = begin(), E = end(); I != E;
41e5dd7070Spatrick        I.MoveToNextPiece())
42e5dd7070Spatrick     os << I.piece();
43e5dd7070Spatrick   return os;
44e5dd7070Spatrick }
45e5dd7070Spatrick 
46e5dd7070Spatrick /// Return true if this character is non-new-line whitespace:
47e5dd7070Spatrick /// ' ', '\\t', '\\f', '\\v', '\\r'.
isWhitespaceExceptNL(unsigned char c)48e5dd7070Spatrick static inline bool isWhitespaceExceptNL(unsigned char c) {
49e5dd7070Spatrick   switch (c) {
50e5dd7070Spatrick   case ' ':
51e5dd7070Spatrick   case '\t':
52e5dd7070Spatrick   case '\f':
53e5dd7070Spatrick   case '\v':
54e5dd7070Spatrick   case '\r':
55e5dd7070Spatrick     return true;
56e5dd7070Spatrick   default:
57e5dd7070Spatrick     return false;
58e5dd7070Spatrick   }
59e5dd7070Spatrick }
60e5dd7070Spatrick 
RemoveText(unsigned OrigOffset,unsigned Size,bool removeLineIfEmpty)61e5dd7070Spatrick void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size,
62e5dd7070Spatrick                                bool removeLineIfEmpty) {
63e5dd7070Spatrick   // Nothing to remove, exit early.
64e5dd7070Spatrick   if (Size == 0) return;
65e5dd7070Spatrick 
66e5dd7070Spatrick   unsigned RealOffset = getMappedOffset(OrigOffset, true);
67e5dd7070Spatrick   assert(RealOffset+Size <= Buffer.size() && "Invalid location");
68e5dd7070Spatrick 
69e5dd7070Spatrick   // Remove the dead characters.
70e5dd7070Spatrick   Buffer.erase(RealOffset, Size);
71e5dd7070Spatrick 
72e5dd7070Spatrick   // Add a delta so that future changes are offset correctly.
73e5dd7070Spatrick   AddReplaceDelta(OrigOffset, -Size);
74e5dd7070Spatrick 
75e5dd7070Spatrick   if (removeLineIfEmpty) {
76e5dd7070Spatrick     // Find the line that the remove occurred and if it is completely empty
77e5dd7070Spatrick     // remove the line as well.
78e5dd7070Spatrick 
79e5dd7070Spatrick     iterator curLineStart = begin();
80e5dd7070Spatrick     unsigned curLineStartOffs = 0;
81e5dd7070Spatrick     iterator posI = begin();
82e5dd7070Spatrick     for (unsigned i = 0; i != RealOffset; ++i) {
83e5dd7070Spatrick       if (*posI == '\n') {
84e5dd7070Spatrick         curLineStart = posI;
85e5dd7070Spatrick         ++curLineStart;
86e5dd7070Spatrick         curLineStartOffs = i + 1;
87e5dd7070Spatrick       }
88e5dd7070Spatrick       ++posI;
89e5dd7070Spatrick     }
90e5dd7070Spatrick 
91e5dd7070Spatrick     unsigned lineSize = 0;
92e5dd7070Spatrick     posI = curLineStart;
93e5dd7070Spatrick     while (posI != end() && isWhitespaceExceptNL(*posI)) {
94e5dd7070Spatrick       ++posI;
95e5dd7070Spatrick       ++lineSize;
96e5dd7070Spatrick     }
97e5dd7070Spatrick     if (posI != end() && *posI == '\n') {
98e5dd7070Spatrick       Buffer.erase(curLineStartOffs, lineSize + 1/* + '\n'*/);
99e5dd7070Spatrick       // FIXME: Here, the offset of the start of the line is supposed to be
100e5dd7070Spatrick       // expressed in terms of the original input not the "real" rewrite
101e5dd7070Spatrick       // buffer.  How do we compute that reliably?  It might be tempting to use
102e5dd7070Spatrick       // curLineStartOffs + OrigOffset - RealOffset, but that assumes the
103e5dd7070Spatrick       // difference between the original and real offset is the same at the
104e5dd7070Spatrick       // removed text and at the start of the line, but that's not true if
105e5dd7070Spatrick       // edits were previously made earlier on the line.  This bug is also
106e5dd7070Spatrick       // documented by a FIXME on the definition of
107e5dd7070Spatrick       // clang::Rewriter::RewriteOptions::RemoveLineIfEmpty.  A reproducer for
108e5dd7070Spatrick       // the implementation below is the test RemoveLineIfEmpty in
109e5dd7070Spatrick       // clang/unittests/Rewrite/RewriteBufferTest.cpp.
110e5dd7070Spatrick       AddReplaceDelta(curLineStartOffs, -(lineSize + 1/* + '\n'*/));
111e5dd7070Spatrick     }
112e5dd7070Spatrick   }
113e5dd7070Spatrick }
114e5dd7070Spatrick 
InsertText(unsigned OrigOffset,StringRef Str,bool InsertAfter)115e5dd7070Spatrick void RewriteBuffer::InsertText(unsigned OrigOffset, StringRef Str,
116e5dd7070Spatrick                                bool InsertAfter) {
117e5dd7070Spatrick   // Nothing to insert, exit early.
118e5dd7070Spatrick   if (Str.empty()) return;
119e5dd7070Spatrick 
120e5dd7070Spatrick   unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter);
121e5dd7070Spatrick   Buffer.insert(RealOffset, Str.begin(), Str.end());
122e5dd7070Spatrick 
123e5dd7070Spatrick   // Add a delta so that future changes are offset correctly.
124e5dd7070Spatrick   AddInsertDelta(OrigOffset, Str.size());
125e5dd7070Spatrick }
126e5dd7070Spatrick 
127e5dd7070Spatrick /// ReplaceText - This method replaces a range of characters in the input
128e5dd7070Spatrick /// buffer with a new string.  This is effectively a combined "remove+insert"
129e5dd7070Spatrick /// operation.
ReplaceText(unsigned OrigOffset,unsigned OrigLength,StringRef NewStr)130e5dd7070Spatrick void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength,
131e5dd7070Spatrick                                 StringRef NewStr) {
132e5dd7070Spatrick   unsigned RealOffset = getMappedOffset(OrigOffset, true);
133e5dd7070Spatrick   Buffer.erase(RealOffset, OrigLength);
134e5dd7070Spatrick   Buffer.insert(RealOffset, NewStr.begin(), NewStr.end());
135e5dd7070Spatrick   if (OrigLength != NewStr.size())
136e5dd7070Spatrick     AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength);
137e5dd7070Spatrick }
138e5dd7070Spatrick 
139e5dd7070Spatrick //===----------------------------------------------------------------------===//
140e5dd7070Spatrick // Rewriter class
141e5dd7070Spatrick //===----------------------------------------------------------------------===//
142e5dd7070Spatrick 
143e5dd7070Spatrick /// getRangeSize - Return the size in bytes of the specified range if they
144e5dd7070Spatrick /// are in the same file.  If not, this returns -1.
getRangeSize(const CharSourceRange & Range,RewriteOptions opts) const145e5dd7070Spatrick int Rewriter::getRangeSize(const CharSourceRange &Range,
146e5dd7070Spatrick                            RewriteOptions opts) const {
147e5dd7070Spatrick   if (!isRewritable(Range.getBegin()) ||
148e5dd7070Spatrick       !isRewritable(Range.getEnd())) return -1;
149e5dd7070Spatrick 
150e5dd7070Spatrick   FileID StartFileID, EndFileID;
151e5dd7070Spatrick   unsigned StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
152e5dd7070Spatrick   unsigned EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
153e5dd7070Spatrick 
154e5dd7070Spatrick   if (StartFileID != EndFileID)
155e5dd7070Spatrick     return -1;
156e5dd7070Spatrick 
157e5dd7070Spatrick   // If edits have been made to this buffer, the delta between the range may
158e5dd7070Spatrick   // have changed.
159e5dd7070Spatrick   std::map<FileID, RewriteBuffer>::const_iterator I =
160e5dd7070Spatrick     RewriteBuffers.find(StartFileID);
161e5dd7070Spatrick   if (I != RewriteBuffers.end()) {
162e5dd7070Spatrick     const RewriteBuffer &RB = I->second;
163e5dd7070Spatrick     EndOff = RB.getMappedOffset(EndOff, opts.IncludeInsertsAtEndOfRange);
164e5dd7070Spatrick     StartOff = RB.getMappedOffset(StartOff, !opts.IncludeInsertsAtBeginOfRange);
165e5dd7070Spatrick   }
166e5dd7070Spatrick 
167e5dd7070Spatrick   // Adjust the end offset to the end of the last token, instead of being the
168e5dd7070Spatrick   // start of the last token if this is a token range.
169e5dd7070Spatrick   if (Range.isTokenRange())
170e5dd7070Spatrick     EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
171e5dd7070Spatrick 
172e5dd7070Spatrick   return EndOff-StartOff;
173e5dd7070Spatrick }
174e5dd7070Spatrick 
getRangeSize(SourceRange Range,RewriteOptions opts) const175e5dd7070Spatrick int Rewriter::getRangeSize(SourceRange Range, RewriteOptions opts) const {
176e5dd7070Spatrick   return getRangeSize(CharSourceRange::getTokenRange(Range), opts);
177e5dd7070Spatrick }
178e5dd7070Spatrick 
179e5dd7070Spatrick /// getRewrittenText - Return the rewritten form of the text in the specified
180e5dd7070Spatrick /// range.  If the start or end of the range was unrewritable or if they are
181e5dd7070Spatrick /// in different buffers, this returns an empty string.
182e5dd7070Spatrick ///
183e5dd7070Spatrick /// Note that this method is not particularly efficient.
getRewrittenText(CharSourceRange Range) const184e5dd7070Spatrick std::string Rewriter::getRewrittenText(CharSourceRange Range) const {
185e5dd7070Spatrick   if (!isRewritable(Range.getBegin()) ||
186e5dd7070Spatrick       !isRewritable(Range.getEnd()))
187e5dd7070Spatrick     return {};
188e5dd7070Spatrick 
189e5dd7070Spatrick   FileID StartFileID, EndFileID;
190e5dd7070Spatrick   unsigned StartOff, EndOff;
191e5dd7070Spatrick   StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
192e5dd7070Spatrick   EndOff   = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
193e5dd7070Spatrick 
194e5dd7070Spatrick   if (StartFileID != EndFileID)
195e5dd7070Spatrick     return {}; // Start and end in different buffers.
196e5dd7070Spatrick 
197e5dd7070Spatrick   // If edits have been made to this buffer, the delta between the range may
198e5dd7070Spatrick   // have changed.
199e5dd7070Spatrick   std::map<FileID, RewriteBuffer>::const_iterator I =
200e5dd7070Spatrick     RewriteBuffers.find(StartFileID);
201e5dd7070Spatrick   if (I == RewriteBuffers.end()) {
202e5dd7070Spatrick     // If the buffer hasn't been rewritten, just return the text from the input.
203e5dd7070Spatrick     const char *Ptr = SourceMgr->getCharacterData(Range.getBegin());
204e5dd7070Spatrick 
205e5dd7070Spatrick     // Adjust the end offset to the end of the last token, instead of being the
206e5dd7070Spatrick     // start of the last token.
207e5dd7070Spatrick     if (Range.isTokenRange())
208e5dd7070Spatrick       EndOff +=
209e5dd7070Spatrick           Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
210e5dd7070Spatrick     return std::string(Ptr, Ptr+EndOff-StartOff);
211e5dd7070Spatrick   }
212e5dd7070Spatrick 
213e5dd7070Spatrick   const RewriteBuffer &RB = I->second;
214e5dd7070Spatrick   EndOff = RB.getMappedOffset(EndOff, true);
215e5dd7070Spatrick   StartOff = RB.getMappedOffset(StartOff);
216e5dd7070Spatrick 
217e5dd7070Spatrick   // Adjust the end offset to the end of the last token, instead of being the
218e5dd7070Spatrick   // start of the last token.
219e5dd7070Spatrick   if (Range.isTokenRange())
220e5dd7070Spatrick     EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
221e5dd7070Spatrick 
222e5dd7070Spatrick   // Advance the iterators to the right spot, yay for linear time algorithms.
223e5dd7070Spatrick   RewriteBuffer::iterator Start = RB.begin();
224e5dd7070Spatrick   std::advance(Start, StartOff);
225e5dd7070Spatrick   RewriteBuffer::iterator End = Start;
226*12c85518Srobert   assert(EndOff >= StartOff && "Invalid iteration distance");
227e5dd7070Spatrick   std::advance(End, EndOff-StartOff);
228e5dd7070Spatrick 
229e5dd7070Spatrick   return std::string(Start, End);
230e5dd7070Spatrick }
231e5dd7070Spatrick 
getLocationOffsetAndFileID(SourceLocation Loc,FileID & FID) const232e5dd7070Spatrick unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc,
233e5dd7070Spatrick                                               FileID &FID) const {
234e5dd7070Spatrick   assert(Loc.isValid() && "Invalid location");
235e5dd7070Spatrick   std::pair<FileID, unsigned> V = SourceMgr->getDecomposedLoc(Loc);
236e5dd7070Spatrick   FID = V.first;
237e5dd7070Spatrick   return V.second;
238e5dd7070Spatrick }
239e5dd7070Spatrick 
240e5dd7070Spatrick /// getEditBuffer - Get or create a RewriteBuffer for the specified FileID.
getEditBuffer(FileID FID)241e5dd7070Spatrick RewriteBuffer &Rewriter::getEditBuffer(FileID FID) {
242e5dd7070Spatrick   std::map<FileID, RewriteBuffer>::iterator I =
243e5dd7070Spatrick     RewriteBuffers.lower_bound(FID);
244e5dd7070Spatrick   if (I != RewriteBuffers.end() && I->first == FID)
245e5dd7070Spatrick     return I->second;
246e5dd7070Spatrick   I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer()));
247e5dd7070Spatrick 
248e5dd7070Spatrick   StringRef MB = SourceMgr->getBufferData(FID);
249e5dd7070Spatrick   I->second.Initialize(MB.begin(), MB.end());
250e5dd7070Spatrick 
251e5dd7070Spatrick   return I->second;
252e5dd7070Spatrick }
253e5dd7070Spatrick 
254e5dd7070Spatrick /// InsertText - Insert the specified string at the specified location in the
255e5dd7070Spatrick /// original buffer.
InsertText(SourceLocation Loc,StringRef Str,bool InsertAfter,bool indentNewLines)256e5dd7070Spatrick bool Rewriter::InsertText(SourceLocation Loc, StringRef Str,
257e5dd7070Spatrick                           bool InsertAfter, bool indentNewLines) {
258e5dd7070Spatrick   if (!isRewritable(Loc)) return true;
259e5dd7070Spatrick   FileID FID;
260e5dd7070Spatrick   unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);
261e5dd7070Spatrick 
262e5dd7070Spatrick   SmallString<128> indentedStr;
263*12c85518Srobert   if (indentNewLines && Str.contains('\n')) {
264e5dd7070Spatrick     StringRef MB = SourceMgr->getBufferData(FID);
265e5dd7070Spatrick 
266e5dd7070Spatrick     unsigned lineNo = SourceMgr->getLineNumber(FID, StartOffs) - 1;
267a9ac8606Spatrick     const SrcMgr::ContentCache *Content =
268a9ac8606Spatrick         &SourceMgr->getSLocEntry(FID).getFile().getContentCache();
269e5dd7070Spatrick     unsigned lineOffs = Content->SourceLineCache[lineNo];
270e5dd7070Spatrick 
271e5dd7070Spatrick     // Find the whitespace at the start of the line.
272e5dd7070Spatrick     StringRef indentSpace;
273e5dd7070Spatrick     {
274e5dd7070Spatrick       unsigned i = lineOffs;
275e5dd7070Spatrick       while (isWhitespaceExceptNL(MB[i]))
276e5dd7070Spatrick         ++i;
277e5dd7070Spatrick       indentSpace = MB.substr(lineOffs, i-lineOffs);
278e5dd7070Spatrick     }
279e5dd7070Spatrick 
280e5dd7070Spatrick     SmallVector<StringRef, 4> lines;
281e5dd7070Spatrick     Str.split(lines, "\n");
282e5dd7070Spatrick 
283e5dd7070Spatrick     for (unsigned i = 0, e = lines.size(); i != e; ++i) {
284e5dd7070Spatrick       indentedStr += lines[i];
285e5dd7070Spatrick       if (i < e-1) {
286e5dd7070Spatrick         indentedStr += '\n';
287e5dd7070Spatrick         indentedStr += indentSpace;
288e5dd7070Spatrick       }
289e5dd7070Spatrick     }
290e5dd7070Spatrick     Str = indentedStr.str();
291e5dd7070Spatrick   }
292e5dd7070Spatrick 
293e5dd7070Spatrick   getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter);
294e5dd7070Spatrick   return false;
295e5dd7070Spatrick }
296e5dd7070Spatrick 
InsertTextAfterToken(SourceLocation Loc,StringRef Str)297e5dd7070Spatrick bool Rewriter::InsertTextAfterToken(SourceLocation Loc, StringRef Str) {
298e5dd7070Spatrick   if (!isRewritable(Loc)) return true;
299e5dd7070Spatrick   FileID FID;
300e5dd7070Spatrick   unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);
301e5dd7070Spatrick   RewriteOptions rangeOpts;
302e5dd7070Spatrick   rangeOpts.IncludeInsertsAtBeginOfRange = false;
303e5dd7070Spatrick   StartOffs += getRangeSize(SourceRange(Loc, Loc), rangeOpts);
304e5dd7070Spatrick   getEditBuffer(FID).InsertText(StartOffs, Str, /*InsertAfter*/true);
305e5dd7070Spatrick   return false;
306e5dd7070Spatrick }
307e5dd7070Spatrick 
308e5dd7070Spatrick /// RemoveText - Remove the specified text region.
RemoveText(SourceLocation Start,unsigned Length,RewriteOptions opts)309e5dd7070Spatrick bool Rewriter::RemoveText(SourceLocation Start, unsigned Length,
310e5dd7070Spatrick                           RewriteOptions opts) {
311e5dd7070Spatrick   if (!isRewritable(Start)) return true;
312e5dd7070Spatrick   FileID FID;
313e5dd7070Spatrick   unsigned StartOffs = getLocationOffsetAndFileID(Start, FID);
314e5dd7070Spatrick   getEditBuffer(FID).RemoveText(StartOffs, Length, opts.RemoveLineIfEmpty);
315e5dd7070Spatrick   return false;
316e5dd7070Spatrick }
317e5dd7070Spatrick 
318e5dd7070Spatrick /// ReplaceText - This method replaces a range of characters in the input
319e5dd7070Spatrick /// buffer with a new string.  This is effectively a combined "remove/insert"
320e5dd7070Spatrick /// operation.
ReplaceText(SourceLocation Start,unsigned OrigLength,StringRef NewStr)321e5dd7070Spatrick bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength,
322e5dd7070Spatrick                            StringRef NewStr) {
323e5dd7070Spatrick   if (!isRewritable(Start)) return true;
324e5dd7070Spatrick   FileID StartFileID;
325e5dd7070Spatrick   unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID);
326e5dd7070Spatrick 
327e5dd7070Spatrick   getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, NewStr);
328e5dd7070Spatrick   return false;
329e5dd7070Spatrick }
330e5dd7070Spatrick 
ReplaceText(SourceRange range,SourceRange replacementRange)331e5dd7070Spatrick bool Rewriter::ReplaceText(SourceRange range, SourceRange replacementRange) {
332e5dd7070Spatrick   if (!isRewritable(range.getBegin())) return true;
333e5dd7070Spatrick   if (!isRewritable(range.getEnd())) return true;
334e5dd7070Spatrick   if (replacementRange.isInvalid()) return true;
335e5dd7070Spatrick   SourceLocation start = range.getBegin();
336e5dd7070Spatrick   unsigned origLength = getRangeSize(range);
337e5dd7070Spatrick   unsigned newLength = getRangeSize(replacementRange);
338e5dd7070Spatrick   FileID FID;
339e5dd7070Spatrick   unsigned newOffs = getLocationOffsetAndFileID(replacementRange.getBegin(),
340e5dd7070Spatrick                                                 FID);
341e5dd7070Spatrick   StringRef MB = SourceMgr->getBufferData(FID);
342e5dd7070Spatrick   return ReplaceText(start, origLength, MB.substr(newOffs, newLength));
343e5dd7070Spatrick }
344e5dd7070Spatrick 
IncreaseIndentation(CharSourceRange range,SourceLocation parentIndent)345e5dd7070Spatrick bool Rewriter::IncreaseIndentation(CharSourceRange range,
346e5dd7070Spatrick                                    SourceLocation parentIndent) {
347e5dd7070Spatrick   if (range.isInvalid()) return true;
348e5dd7070Spatrick   if (!isRewritable(range.getBegin())) return true;
349e5dd7070Spatrick   if (!isRewritable(range.getEnd())) return true;
350e5dd7070Spatrick   if (!isRewritable(parentIndent)) return true;
351e5dd7070Spatrick 
352e5dd7070Spatrick   FileID StartFileID, EndFileID, parentFileID;
353e5dd7070Spatrick   unsigned StartOff, EndOff, parentOff;
354e5dd7070Spatrick 
355e5dd7070Spatrick   StartOff = getLocationOffsetAndFileID(range.getBegin(), StartFileID);
356e5dd7070Spatrick   EndOff   = getLocationOffsetAndFileID(range.getEnd(), EndFileID);
357e5dd7070Spatrick   parentOff = getLocationOffsetAndFileID(parentIndent, parentFileID);
358e5dd7070Spatrick 
359e5dd7070Spatrick   if (StartFileID != EndFileID || StartFileID != parentFileID)
360e5dd7070Spatrick     return true;
361e5dd7070Spatrick   if (StartOff > EndOff)
362e5dd7070Spatrick     return true;
363e5dd7070Spatrick 
364e5dd7070Spatrick   FileID FID = StartFileID;
365e5dd7070Spatrick   StringRef MB = SourceMgr->getBufferData(FID);
366e5dd7070Spatrick 
367e5dd7070Spatrick   unsigned parentLineNo = SourceMgr->getLineNumber(FID, parentOff) - 1;
368e5dd7070Spatrick   unsigned startLineNo = SourceMgr->getLineNumber(FID, StartOff) - 1;
369e5dd7070Spatrick   unsigned endLineNo = SourceMgr->getLineNumber(FID, EndOff) - 1;
370e5dd7070Spatrick 
371a9ac8606Spatrick   const SrcMgr::ContentCache *Content =
372a9ac8606Spatrick       &SourceMgr->getSLocEntry(FID).getFile().getContentCache();
373e5dd7070Spatrick 
374e5dd7070Spatrick   // Find where the lines start.
375e5dd7070Spatrick   unsigned parentLineOffs = Content->SourceLineCache[parentLineNo];
376e5dd7070Spatrick   unsigned startLineOffs = Content->SourceLineCache[startLineNo];
377e5dd7070Spatrick 
378e5dd7070Spatrick   // Find the whitespace at the start of each line.
379e5dd7070Spatrick   StringRef parentSpace, startSpace;
380e5dd7070Spatrick   {
381e5dd7070Spatrick     unsigned i = parentLineOffs;
382e5dd7070Spatrick     while (isWhitespaceExceptNL(MB[i]))
383e5dd7070Spatrick       ++i;
384e5dd7070Spatrick     parentSpace = MB.substr(parentLineOffs, i-parentLineOffs);
385e5dd7070Spatrick 
386e5dd7070Spatrick     i = startLineOffs;
387e5dd7070Spatrick     while (isWhitespaceExceptNL(MB[i]))
388e5dd7070Spatrick       ++i;
389e5dd7070Spatrick     startSpace = MB.substr(startLineOffs, i-startLineOffs);
390e5dd7070Spatrick   }
391e5dd7070Spatrick   if (parentSpace.size() >= startSpace.size())
392e5dd7070Spatrick     return true;
393e5dd7070Spatrick   if (!startSpace.startswith(parentSpace))
394e5dd7070Spatrick     return true;
395e5dd7070Spatrick 
396e5dd7070Spatrick   StringRef indent = startSpace.substr(parentSpace.size());
397e5dd7070Spatrick 
398e5dd7070Spatrick   // Indent the lines between start/end offsets.
399e5dd7070Spatrick   RewriteBuffer &RB = getEditBuffer(FID);
400e5dd7070Spatrick   for (unsigned lineNo = startLineNo; lineNo <= endLineNo; ++lineNo) {
401e5dd7070Spatrick     unsigned offs = Content->SourceLineCache[lineNo];
402e5dd7070Spatrick     unsigned i = offs;
403e5dd7070Spatrick     while (isWhitespaceExceptNL(MB[i]))
404e5dd7070Spatrick       ++i;
405e5dd7070Spatrick     StringRef origIndent = MB.substr(offs, i-offs);
406e5dd7070Spatrick     if (origIndent.startswith(startSpace))
407e5dd7070Spatrick       RB.InsertText(offs, indent, /*InsertAfter=*/false);
408e5dd7070Spatrick   }
409e5dd7070Spatrick 
410e5dd7070Spatrick   return false;
411e5dd7070Spatrick }
412e5dd7070Spatrick 
413e5dd7070Spatrick namespace {
414e5dd7070Spatrick 
415e5dd7070Spatrick // A wrapper for a file stream that atomically overwrites the target.
416e5dd7070Spatrick //
417e5dd7070Spatrick // Creates a file output stream for a temporary file in the constructor,
418e5dd7070Spatrick // which is later accessible via getStream() if ok() return true.
419e5dd7070Spatrick // Flushes the stream and moves the temporary file to the target location
420e5dd7070Spatrick // in the destructor.
421e5dd7070Spatrick class AtomicallyMovedFile {
422e5dd7070Spatrick public:
AtomicallyMovedFile(DiagnosticsEngine & Diagnostics,StringRef Filename,bool & AllWritten)423e5dd7070Spatrick   AtomicallyMovedFile(DiagnosticsEngine &Diagnostics, StringRef Filename,
424e5dd7070Spatrick                       bool &AllWritten)
425e5dd7070Spatrick       : Diagnostics(Diagnostics), Filename(Filename), AllWritten(AllWritten) {
426e5dd7070Spatrick     TempFilename = Filename;
427e5dd7070Spatrick     TempFilename += "-%%%%%%%%";
428e5dd7070Spatrick     int FD;
429e5dd7070Spatrick     if (llvm::sys::fs::createUniqueFile(TempFilename, FD, TempFilename)) {
430e5dd7070Spatrick       AllWritten = false;
431e5dd7070Spatrick       Diagnostics.Report(clang::diag::err_unable_to_make_temp)
432e5dd7070Spatrick         << TempFilename;
433e5dd7070Spatrick     } else {
434e5dd7070Spatrick       FileStream.reset(new llvm::raw_fd_ostream(FD, /*shouldClose=*/true));
435e5dd7070Spatrick     }
436e5dd7070Spatrick   }
437e5dd7070Spatrick 
~AtomicallyMovedFile()438e5dd7070Spatrick   ~AtomicallyMovedFile() {
439e5dd7070Spatrick     if (!ok()) return;
440e5dd7070Spatrick 
441e5dd7070Spatrick     // Close (will also flush) theFileStream.
442e5dd7070Spatrick     FileStream->close();
443e5dd7070Spatrick     if (std::error_code ec = llvm::sys::fs::rename(TempFilename, Filename)) {
444e5dd7070Spatrick       AllWritten = false;
445e5dd7070Spatrick       Diagnostics.Report(clang::diag::err_unable_to_rename_temp)
446e5dd7070Spatrick         << TempFilename << Filename << ec.message();
447e5dd7070Spatrick       // If the remove fails, there's not a lot we can do - this is already an
448e5dd7070Spatrick       // error.
449e5dd7070Spatrick       llvm::sys::fs::remove(TempFilename);
450e5dd7070Spatrick     }
451e5dd7070Spatrick   }
452e5dd7070Spatrick 
ok()453e5dd7070Spatrick   bool ok() { return (bool)FileStream; }
getStream()454e5dd7070Spatrick   raw_ostream &getStream() { return *FileStream; }
455e5dd7070Spatrick 
456e5dd7070Spatrick private:
457e5dd7070Spatrick   DiagnosticsEngine &Diagnostics;
458e5dd7070Spatrick   StringRef Filename;
459e5dd7070Spatrick   SmallString<128> TempFilename;
460e5dd7070Spatrick   std::unique_ptr<llvm::raw_fd_ostream> FileStream;
461e5dd7070Spatrick   bool &AllWritten;
462e5dd7070Spatrick };
463e5dd7070Spatrick 
464e5dd7070Spatrick } // namespace
465e5dd7070Spatrick 
overwriteChangedFiles()466e5dd7070Spatrick bool Rewriter::overwriteChangedFiles() {
467e5dd7070Spatrick   bool AllWritten = true;
468e5dd7070Spatrick   for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) {
469e5dd7070Spatrick     const FileEntry *Entry =
470e5dd7070Spatrick         getSourceMgr().getFileEntryForID(I->first);
471e5dd7070Spatrick     AtomicallyMovedFile File(getSourceMgr().getDiagnostics(), Entry->getName(),
472e5dd7070Spatrick                              AllWritten);
473e5dd7070Spatrick     if (File.ok()) {
474e5dd7070Spatrick       I->second.write(File.getStream());
475e5dd7070Spatrick     }
476e5dd7070Spatrick   }
477e5dd7070Spatrick   return !AllWritten;
478e5dd7070Spatrick }
479