1 //===--- Replacement.cpp - Framework for clang refactoring tools ----------===// 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 // Implements classes to support/store refactorings. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/Tooling/Core/Replacement.h" 15 16 #include "clang/Basic/Diagnostic.h" 17 #include "clang/Basic/DiagnosticIDs.h" 18 #include "clang/Basic/DiagnosticOptions.h" 19 #include "clang/Basic/FileManager.h" 20 #include "clang/Basic/SourceManager.h" 21 #include "clang/Lex/Lexer.h" 22 #include "clang/Rewrite/Core/Rewriter.h" 23 #include "llvm/ADT/SmallPtrSet.h" 24 #include "llvm/Support/FileSystem.h" 25 #include "llvm/Support/Path.h" 26 #include "llvm/Support/raw_os_ostream.h" 27 28 namespace clang { 29 namespace tooling { 30 31 static const char * const InvalidLocation = ""; 32 33 Replacement::Replacement() 34 : FilePath(InvalidLocation) {} 35 36 Replacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length, 37 StringRef ReplacementText) 38 : FilePath(FilePath), ReplacementRange(Offset, Length), 39 ReplacementText(ReplacementText) {} 40 41 Replacement::Replacement(const SourceManager &Sources, SourceLocation Start, 42 unsigned Length, StringRef ReplacementText) { 43 setFromSourceLocation(Sources, Start, Length, ReplacementText); 44 } 45 46 Replacement::Replacement(const SourceManager &Sources, 47 const CharSourceRange &Range, 48 StringRef ReplacementText, 49 const LangOptions &LangOpts) { 50 setFromSourceRange(Sources, Range, ReplacementText, LangOpts); 51 } 52 53 bool Replacement::isApplicable() const { 54 return FilePath != InvalidLocation; 55 } 56 57 bool Replacement::apply(Rewriter &Rewrite) const { 58 SourceManager &SM = Rewrite.getSourceMgr(); 59 const FileEntry *Entry = SM.getFileManager().getFile(FilePath); 60 if (!Entry) 61 return false; 62 63 FileID ID = SM.getOrCreateFileID(Entry, SrcMgr::C_User); 64 const SourceLocation Start = 65 SM.getLocForStartOfFile(ID). 66 getLocWithOffset(ReplacementRange.getOffset()); 67 // ReplaceText returns false on success. 68 // ReplaceText only fails if the source location is not a file location, in 69 // which case we already returned false earlier. 70 bool RewriteSucceeded = !Rewrite.ReplaceText( 71 Start, ReplacementRange.getLength(), ReplacementText); 72 assert(RewriteSucceeded); 73 return RewriteSucceeded; 74 } 75 76 std::string Replacement::toString() const { 77 std::string Result; 78 llvm::raw_string_ostream Stream(Result); 79 Stream << FilePath << ": " << ReplacementRange.getOffset() << ":+" 80 << ReplacementRange.getLength() << ":\"" << ReplacementText << "\""; 81 return Stream.str(); 82 } 83 84 bool operator<(const Replacement &LHS, const Replacement &RHS) { 85 if (LHS.getOffset() != RHS.getOffset()) 86 return LHS.getOffset() < RHS.getOffset(); 87 88 if (LHS.getLength() != RHS.getLength()) 89 return LHS.getLength() < RHS.getLength(); 90 91 if (LHS.getFilePath() != RHS.getFilePath()) 92 return LHS.getFilePath() < RHS.getFilePath(); 93 return LHS.getReplacementText() < RHS.getReplacementText(); 94 } 95 96 bool operator==(const Replacement &LHS, const Replacement &RHS) { 97 return LHS.getOffset() == RHS.getOffset() && 98 LHS.getLength() == RHS.getLength() && 99 LHS.getFilePath() == RHS.getFilePath() && 100 LHS.getReplacementText() == RHS.getReplacementText(); 101 } 102 103 void Replacement::setFromSourceLocation(const SourceManager &Sources, 104 SourceLocation Start, unsigned Length, 105 StringRef ReplacementText) { 106 const std::pair<FileID, unsigned> DecomposedLocation = 107 Sources.getDecomposedLoc(Start); 108 const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first); 109 this->FilePath = Entry ? Entry->getName() : InvalidLocation; 110 this->ReplacementRange = Range(DecomposedLocation.second, Length); 111 this->ReplacementText = ReplacementText; 112 } 113 114 // FIXME: This should go into the Lexer, but we need to figure out how 115 // to handle ranges for refactoring in general first - there is no obvious 116 // good way how to integrate this into the Lexer yet. 117 static int getRangeSize(const SourceManager &Sources, 118 const CharSourceRange &Range, 119 const LangOptions &LangOpts) { 120 SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin()); 121 SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd()); 122 std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin); 123 std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd); 124 if (Start.first != End.first) return -1; 125 if (Range.isTokenRange()) 126 End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources, LangOpts); 127 return End.second - Start.second; 128 } 129 130 void Replacement::setFromSourceRange(const SourceManager &Sources, 131 const CharSourceRange &Range, 132 StringRef ReplacementText, 133 const LangOptions &LangOpts) { 134 setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()), 135 getRangeSize(Sources, Range, LangOpts), 136 ReplacementText); 137 } 138 139 Replacement 140 Replacements::getReplacementInChangedCode(const Replacement &R) const { 141 unsigned NewStart = getShiftedCodePosition(R.getOffset()); 142 unsigned NewEnd = getShiftedCodePosition(R.getOffset() + R.getLength()); 143 return Replacement(R.getFilePath(), NewStart, NewEnd - NewStart, 144 R.getReplacementText()); 145 } 146 147 static llvm::Error makeConflictReplacementsError(const Replacement &New, 148 const Replacement &Existing) { 149 return llvm::make_error<llvm::StringError>( 150 "New replacement:\n" + New.toString() + 151 "\nconflicts with existing replacement:\n" + Existing.toString(), 152 llvm::inconvertibleErrorCode()); 153 } 154 155 Replacements Replacements::getCanonicalReplacements() const { 156 std::vector<Replacement> NewReplaces; 157 // Merge adjacent replacements. 158 for (const auto &R : Replaces) { 159 if (NewReplaces.empty()) { 160 NewReplaces.push_back(R); 161 continue; 162 } 163 auto &Prev = NewReplaces.back(); 164 unsigned PrevEnd = Prev.getOffset() + Prev.getLength(); 165 if (PrevEnd < R.getOffset()) { 166 NewReplaces.push_back(R); 167 } else { 168 assert(PrevEnd == R.getOffset() && 169 "Existing replacements must not overlap."); 170 Replacement NewR( 171 R.getFilePath(), Prev.getOffset(), Prev.getLength() + R.getLength(), 172 (Prev.getReplacementText() + R.getReplacementText()).str()); 173 Prev = NewR; 174 } 175 } 176 ReplacementsImpl NewReplacesImpl(NewReplaces.begin(), NewReplaces.end()); 177 return Replacements(NewReplacesImpl.begin(), NewReplacesImpl.end()); 178 } 179 180 // `R` and `Replaces` are order-independent if applying them in either order 181 // has the same effect, so we need to compare replacements associated to 182 // applying them in either order. 183 llvm::Expected<Replacements> 184 Replacements::mergeIfOrderIndependent(const Replacement &R) const { 185 Replacements Rs(R); 186 // A Replacements set containg a single replacement that is `R` referring to 187 // the code after the existing replacements `Replaces` are applied. 188 Replacements RsShiftedByReplaces(getReplacementInChangedCode(R)); 189 // A Replacements set that is `Replaces` referring to the code after `R` is 190 // applied. 191 Replacements ReplacesShiftedByRs; 192 for (const auto &Replace : Replaces) 193 ReplacesShiftedByRs.Replaces.insert( 194 Rs.getReplacementInChangedCode(Replace)); 195 // This is equivalent to applying `Replaces` first and then `R`. 196 auto MergeShiftedRs = merge(RsShiftedByReplaces); 197 // This is equivalent to applying `R` first and then `Replaces`. 198 auto MergeShiftedReplaces = Rs.merge(ReplacesShiftedByRs); 199 200 // Since empty or segmented replacements around existing replacements might be 201 // produced above, we need to compare replacements in canonical forms. 202 if (MergeShiftedRs.getCanonicalReplacements() == 203 MergeShiftedReplaces.getCanonicalReplacements()) 204 return MergeShiftedRs; 205 return makeConflictReplacementsError(R, *Replaces.begin()); 206 } 207 208 llvm::Error Replacements::add(const Replacement &R) { 209 // Check the file path. 210 if (!Replaces.empty() && R.getFilePath() != Replaces.begin()->getFilePath()) 211 return llvm::make_error<llvm::StringError>( 212 "All replacements must have the same file path. New replacement: " + 213 R.getFilePath() + ", existing replacements: " + 214 Replaces.begin()->getFilePath() + "\n", 215 llvm::inconvertibleErrorCode()); 216 217 // Special-case header insertions. 218 if (R.getOffset() == UINT_MAX) { 219 Replaces.insert(R); 220 return llvm::Error::success(); 221 } 222 223 // This replacement cannot conflict with replacements that end before 224 // this replacement starts or start after this replacement ends. 225 // We also know that there currently are no overlapping replacements. 226 // Thus, we know that all replacements that start after the end of the current 227 // replacement cannot overlap. 228 Replacement AtEnd(R.getFilePath(), R.getOffset() + R.getLength(), 0, ""); 229 230 // Find the first entry that starts after or at the end of R. Note that 231 // entries that start at the end can still be conflicting if R is an 232 // insertion. 233 auto I = Replaces.lower_bound(AtEnd); 234 // If `I` starts at the same offset as `R`, `R` must be an insertion. 235 if (I != Replaces.end() && R.getOffset() == I->getOffset()) { 236 assert(R.getLength() == 0); 237 // `I` is also an insertion, `R` and `I` conflict. 238 if (I->getLength() == 0) { 239 // Check if two insertions are order-indepedent: if inserting them in 240 // either order produces the same text, they are order-independent. 241 if ((R.getReplacementText() + I->getReplacementText()).str() != 242 (I->getReplacementText() + R.getReplacementText()).str()) { 243 return makeConflictReplacementsError(R, *I); 244 } 245 // If insertions are order-independent, we can merge them. 246 Replacement NewR( 247 R.getFilePath(), R.getOffset(), 0, 248 (R.getReplacementText() + I->getReplacementText()).str()); 249 Replaces.erase(I); 250 Replaces.insert(std::move(NewR)); 251 return llvm::Error::success(); 252 } 253 // Insertion `R` is adjacent to a non-insertion replacement `I`, so they 254 // are order-independent. It is safe to assume that `R` will not conflict 255 // with any replacement before `I` since all replacements before `I` must 256 // either end before `R` or end at `R` but has length > 0 (if the 257 // replacement before `I` is an insertion at `R`, it would have been `I` 258 // since it is a lower bound of `AtEnd` and ordered before the current `I` 259 // in the set). 260 Replaces.insert(R); 261 return llvm::Error::success(); 262 } 263 264 // `I` is the smallest iterator (after `R`) whose entry cannot overlap. 265 // If that is begin(), there are no overlaps. 266 if (I == Replaces.begin()) { 267 Replaces.insert(R); 268 return llvm::Error::success(); 269 } 270 --I; 271 auto Overlap = [](const Replacement &R1, const Replacement &R2) -> bool { 272 return Range(R1.getOffset(), R1.getLength()) 273 .overlapsWith(Range(R2.getOffset(), R2.getLength())); 274 }; 275 // If the previous entry does not overlap, we know that entries before it 276 // can also not overlap. 277 if (!Overlap(R, *I)) { 278 // If `R` and `I` do not have the same offset, it is safe to add `R` since 279 // it must come after `I`. Otherwise: 280 // - If `R` is an insertion, `I` must not be an insertion since it would 281 // have come after `AtEnd`. 282 // - If `R` is not an insertion, `I` must be an insertion; otherwise, `R` 283 // and `I` would have overlapped. 284 // In either case, we can safely insert `R`. 285 Replaces.insert(R); 286 } else { 287 // `I` overlaps with `R`. We need to check `R` against all overlapping 288 // replacements to see if they are order-indepedent. If they are, merge `R` 289 // with them and replace them with the merged replacements. 290 auto MergeBegin = I; 291 auto MergeEnd = std::next(I); 292 while (I != Replaces.begin()) { 293 --I; 294 // If `I` doesn't overlap with `R`, don't merge it. 295 if (!Overlap(R, *I)) 296 break; 297 MergeBegin = I; 298 } 299 Replacements OverlapReplaces(MergeBegin, MergeEnd); 300 llvm::Expected<Replacements> Merged = 301 OverlapReplaces.mergeIfOrderIndependent(R); 302 if (!Merged) 303 return Merged.takeError(); 304 Replaces.erase(MergeBegin, MergeEnd); 305 Replaces.insert(Merged->begin(), Merged->end()); 306 } 307 return llvm::Error::success(); 308 } 309 310 namespace { 311 312 // Represents a merged replacement, i.e. a replacement consisting of multiple 313 // overlapping replacements from 'First' and 'Second' in mergeReplacements. 314 // 315 // Position projection: 316 // Offsets and lengths of the replacements can generally refer to two different 317 // coordinate spaces. Replacements from 'First' refer to the original text 318 // whereas replacements from 'Second' refer to the text after applying 'First'. 319 // 320 // MergedReplacement always operates in the coordinate space of the original 321 // text, i.e. transforms elements from 'Second' to take into account what was 322 // changed based on the elements from 'First'. 323 // 324 // We can correctly calculate this projection as we look at the replacements in 325 // order of strictly increasing offsets. 326 // 327 // Invariants: 328 // * We always merge elements from 'First' into elements from 'Second' and vice 329 // versa. Within each set, the replacements are non-overlapping. 330 // * We only extend to the right, i.e. merge elements with strictly increasing 331 // offsets. 332 class MergedReplacement { 333 public: 334 MergedReplacement(const Replacement &R, bool MergeSecond, int D) 335 : MergeSecond(MergeSecond), Delta(D), FilePath(R.getFilePath()), 336 Offset(R.getOffset() + (MergeSecond ? 0 : Delta)), Length(R.getLength()), 337 Text(R.getReplacementText()) { 338 Delta += MergeSecond ? 0 : Text.size() - Length; 339 DeltaFirst = MergeSecond ? Text.size() - Length : 0; 340 } 341 342 // Merges the next element 'R' into this merged element. As we always merge 343 // from 'First' into 'Second' or vice versa, the MergedReplacement knows what 344 // set the next element is coming from. 345 void merge(const Replacement &R) { 346 if (MergeSecond) { 347 unsigned REnd = R.getOffset() + Delta + R.getLength(); 348 unsigned End = Offset + Text.size(); 349 if (REnd > End) { 350 Length += REnd - End; 351 MergeSecond = false; 352 } 353 StringRef TextRef = Text; 354 StringRef Head = TextRef.substr(0, R.getOffset() + Delta - Offset); 355 StringRef Tail = TextRef.substr(REnd - Offset); 356 Text = (Head + R.getReplacementText() + Tail).str(); 357 Delta += R.getReplacementText().size() - R.getLength(); 358 } else { 359 unsigned End = Offset + Length; 360 StringRef RText = R.getReplacementText(); 361 StringRef Tail = RText.substr(End - R.getOffset()); 362 Text = (Text + Tail).str(); 363 if (R.getOffset() + RText.size() > End) { 364 Length = R.getOffset() + R.getLength() - Offset; 365 MergeSecond = true; 366 } else { 367 Length += R.getLength() - RText.size(); 368 } 369 DeltaFirst += RText.size() - R.getLength(); 370 } 371 } 372 373 // Returns 'true' if 'R' starts strictly after the MergedReplacement and thus 374 // doesn't need to be merged. 375 bool endsBefore(const Replacement &R) const { 376 if (MergeSecond) 377 return Offset + Text.size() < R.getOffset() + Delta; 378 return Offset + Length < R.getOffset(); 379 } 380 381 // Returns 'true' if an element from the second set should be merged next. 382 bool mergeSecond() const { return MergeSecond; } 383 int deltaFirst() const { return DeltaFirst; } 384 Replacement asReplacement() const { return {FilePath, Offset, Length, Text}; } 385 386 private: 387 bool MergeSecond; 388 389 // Amount of characters that elements from 'Second' need to be shifted by in 390 // order to refer to the original text. 391 int Delta; 392 393 // Sum of all deltas (text-length - length) of elements from 'First' merged 394 // into this element. This is used to update 'Delta' once the 395 // MergedReplacement is completed. 396 int DeltaFirst; 397 398 // Data of the actually merged replacement. FilePath and Offset aren't changed 399 // as the element is only extended to the right. 400 const StringRef FilePath; 401 const unsigned Offset; 402 unsigned Length; 403 std::string Text; 404 }; 405 406 } // namespace 407 408 Replacements Replacements::merge(const Replacements &ReplacesToMerge) const { 409 if (empty() || ReplacesToMerge.empty()) 410 return empty() ? ReplacesToMerge : *this; 411 412 auto &First = Replaces; 413 auto &Second = ReplacesToMerge.Replaces; 414 // Delta is the amount of characters that replacements from 'Second' need to 415 // be shifted so that their offsets refer to the original text. 416 int Delta = 0; 417 ReplacementsImpl Result; 418 419 // Iterate over both sets and always add the next element (smallest total 420 // Offset) from either 'First' or 'Second'. Merge that element with 421 // subsequent replacements as long as they overlap. See more details in the 422 // comment on MergedReplacement. 423 for (auto FirstI = First.begin(), SecondI = Second.begin(); 424 FirstI != First.end() || SecondI != Second.end();) { 425 bool NextIsFirst = SecondI == Second.end() || 426 (FirstI != First.end() && 427 FirstI->getOffset() < SecondI->getOffset() + Delta); 428 MergedReplacement Merged(NextIsFirst ? *FirstI : *SecondI, NextIsFirst, 429 Delta); 430 ++(NextIsFirst ? FirstI : SecondI); 431 432 while ((Merged.mergeSecond() && SecondI != Second.end()) || 433 (!Merged.mergeSecond() && FirstI != First.end())) { 434 auto &I = Merged.mergeSecond() ? SecondI : FirstI; 435 if (Merged.endsBefore(*I)) 436 break; 437 Merged.merge(*I); 438 ++I; 439 } 440 Delta -= Merged.deltaFirst(); 441 Result.insert(Merged.asReplacement()); 442 } 443 return Replacements(Result.begin(), Result.end()); 444 } 445 446 // Combines overlapping ranges in \p Ranges and sorts the combined ranges. 447 // Returns a set of non-overlapping and sorted ranges that is equivalent to 448 // \p Ranges. 449 static std::vector<Range> combineAndSortRanges(std::vector<Range> Ranges) { 450 std::sort(Ranges.begin(), Ranges.end(), 451 [](const Range &LHS, const Range &RHS) { 452 if (LHS.getOffset() != RHS.getOffset()) 453 return LHS.getOffset() < RHS.getOffset(); 454 return LHS.getLength() < RHS.getLength(); 455 }); 456 std::vector<Range> Result; 457 for (const auto &R : Ranges) { 458 if (Result.empty() || 459 Result.back().getOffset() + Result.back().getLength() < R.getOffset()) { 460 Result.push_back(R); 461 } else { 462 unsigned NewEnd = 463 std::max(Result.back().getOffset() + Result.back().getLength(), 464 R.getOffset() + R.getLength()); 465 Result[Result.size() - 1] = 466 Range(Result.back().getOffset(), NewEnd - Result.back().getOffset()); 467 } 468 } 469 return Result; 470 } 471 472 std::vector<Range> 473 calculateRangesAfterReplacements(const Replacements &Replaces, 474 const std::vector<Range> &Ranges) { 475 // To calculate the new ranges, 476 // - Turn \p Ranges into Replacements at (offset, length) with an empty 477 // (unimportant) replacement text of length "length". 478 // - Merge with \p Replaces. 479 // - The new ranges will be the affected ranges of the merged replacements. 480 auto MergedRanges = combineAndSortRanges(Ranges); 481 if (Replaces.empty()) 482 return MergedRanges; 483 tooling::Replacements FakeReplaces; 484 for (const auto &R : MergedRanges) { 485 auto Err = FakeReplaces.add(Replacement(Replaces.begin()->getFilePath(), 486 R.getOffset(), R.getLength(), 487 std::string(R.getLength(), ' '))); 488 assert(!Err && 489 "Replacements must not conflict since ranges have been merged."); 490 (void)Err; 491 } 492 return FakeReplaces.merge(Replaces).getAffectedRanges(); 493 } 494 495 std::vector<Range> Replacements::getAffectedRanges() const { 496 std::vector<Range> ChangedRanges; 497 int Shift = 0; 498 for (const Replacement &R : Replaces) { 499 unsigned Offset = R.getOffset() + Shift; 500 unsigned Length = R.getReplacementText().size(); 501 Shift += Length - R.getLength(); 502 ChangedRanges.push_back(Range(Offset, Length)); 503 } 504 return combineAndSortRanges(ChangedRanges); 505 } 506 507 unsigned Replacements::getShiftedCodePosition(unsigned Position) const { 508 unsigned Offset = 0; 509 for (const auto& R : Replaces) { 510 if (R.getOffset() + R.getLength() <= Position) { 511 Offset += R.getReplacementText().size() - R.getLength(); 512 continue; 513 } 514 if (R.getOffset() < Position && 515 R.getOffset() + R.getReplacementText().size() <= Position) { 516 Position = R.getOffset() + R.getReplacementText().size(); 517 if (R.getReplacementText().size() > 0) 518 Position--; 519 } 520 break; 521 } 522 return Position + Offset; 523 } 524 525 bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) { 526 bool Result = true; 527 for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) { 528 if (I->isApplicable()) { 529 Result = I->apply(Rewrite) && Result; 530 } else { 531 Result = false; 532 } 533 } 534 return Result; 535 } 536 537 llvm::Expected<std::string> applyAllReplacements(StringRef Code, 538 const Replacements &Replaces) { 539 if (Replaces.empty()) 540 return Code.str(); 541 542 IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem( 543 new vfs::InMemoryFileSystem); 544 FileManager Files(FileSystemOptions(), InMemoryFileSystem); 545 DiagnosticsEngine Diagnostics( 546 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), 547 new DiagnosticOptions); 548 SourceManager SourceMgr(Diagnostics, Files); 549 Rewriter Rewrite(SourceMgr, LangOptions()); 550 InMemoryFileSystem->addFile( 551 "<stdin>", 0, llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>")); 552 FileID ID = SourceMgr.createFileID(Files.getFile("<stdin>"), SourceLocation(), 553 clang::SrcMgr::C_User); 554 for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) { 555 Replacement Replace("<stdin>", I->getOffset(), I->getLength(), 556 I->getReplacementText()); 557 if (!Replace.apply(Rewrite)) 558 return llvm::make_error<llvm::StringError>( 559 "Failed to apply replacement: " + Replace.toString(), 560 llvm::inconvertibleErrorCode()); 561 } 562 std::string Result; 563 llvm::raw_string_ostream OS(Result); 564 Rewrite.getEditBuffer(ID).write(OS); 565 OS.flush(); 566 return Result; 567 } 568 569 std::map<std::string, Replacements> groupReplacementsByFile( 570 FileManager &FileMgr, 571 const std::map<std::string, Replacements> &FileToReplaces) { 572 std::map<std::string, Replacements> Result; 573 llvm::SmallPtrSet<const FileEntry *, 16> ProcessedFileEntries; 574 for (const auto &Entry : FileToReplaces) { 575 const FileEntry *FE = FileMgr.getFile(Entry.first); 576 if (!FE) 577 llvm::errs() << "File path " << Entry.first << " is invalid.\n"; 578 else if (ProcessedFileEntries.insert(FE).second) 579 Result[Entry.first] = std::move(Entry.second); 580 } 581 return Result; 582 } 583 584 } // end namespace tooling 585 } // end namespace clang 586