| 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 | |