1 //===- DiagnosticRenderer.cpp - Diagnostic Pretty-Printing ----------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "clang/Frontend/DiagnosticRenderer.h" 10 #include "clang/Basic/Diagnostic.h" 11 #include "clang/Basic/DiagnosticOptions.h" 12 #include "clang/Basic/LLVM.h" 13 #include "clang/Basic/SourceLocation.h" 14 #include "clang/Basic/SourceManager.h" 15 #include "clang/Edit/Commit.h" 16 #include "clang/Edit/EditedSource.h" 17 #include "clang/Edit/EditsReceiver.h" 18 #include "clang/Lex/Lexer.h" 19 #include "llvm/ADT/ArrayRef.h" 20 #include "llvm/ADT/DenseMap.h" 21 #include "llvm/ADT/SmallVector.h" 22 #include "llvm/ADT/StringRef.h" 23 #include "llvm/Support/raw_ostream.h" 24 #include <algorithm> 25 #include <cassert> 26 #include <iterator> 27 #include <utility> 28 29 using namespace clang; 30 31 DiagnosticRenderer::DiagnosticRenderer(const LangOptions &LangOpts, 32 DiagnosticOptions &DiagOpts) 33 : LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {} 34 35 DiagnosticRenderer::~DiagnosticRenderer() = default; 36 37 namespace { 38 39 class FixitReceiver : public edit::EditsReceiver { 40 SmallVectorImpl<FixItHint> &MergedFixits; 41 42 public: 43 FixitReceiver(SmallVectorImpl<FixItHint> &MergedFixits) 44 : MergedFixits(MergedFixits) {} 45 46 void insert(SourceLocation loc, StringRef text) override { 47 MergedFixits.push_back(FixItHint::CreateInsertion(loc, text)); 48 } 49 50 void replace(CharSourceRange range, StringRef text) override { 51 MergedFixits.push_back(FixItHint::CreateReplacement(range, text)); 52 } 53 }; 54 55 } // namespace 56 57 static void mergeFixits(ArrayRef<FixItHint> FixItHints, 58 const SourceManager &SM, const LangOptions &LangOpts, 59 SmallVectorImpl<FixItHint> &MergedFixits) { 60 edit::Commit commit(SM, LangOpts); 61 for (const auto &Hint : FixItHints) 62 if (Hint.CodeToInsert.empty()) { 63 if (Hint.InsertFromRange.isValid()) 64 commit.insertFromRange(Hint.RemoveRange.getBegin(), 65 Hint.InsertFromRange, /*afterToken=*/false, 66 Hint.BeforePreviousInsertions); 67 else 68 commit.remove(Hint.RemoveRange); 69 } else { 70 if (Hint.RemoveRange.isTokenRange() || 71 Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd()) 72 commit.replace(Hint.RemoveRange, Hint.CodeToInsert); 73 else 74 commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert, 75 /*afterToken=*/false, Hint.BeforePreviousInsertions); 76 } 77 78 edit::EditedSource Editor(SM, LangOpts); 79 if (Editor.commit(commit)) { 80 FixitReceiver Rec(MergedFixits); 81 Editor.applyRewrites(Rec); 82 } 83 } 84 85 void DiagnosticRenderer::emitDiagnostic(FullSourceLoc Loc, 86 DiagnosticsEngine::Level Level, 87 StringRef Message, 88 ArrayRef<CharSourceRange> Ranges, 89 ArrayRef<FixItHint> FixItHints, 90 DiagOrStoredDiag D) { 91 assert(Loc.hasManager() || Loc.isInvalid()); 92 93 beginDiagnostic(D, Level); 94 95 if (!Loc.isValid()) 96 // If we have no source location, just emit the diagnostic message. 97 emitDiagnosticMessage(Loc, PresumedLoc(), Level, Message, Ranges, D); 98 else { 99 // Get the ranges into a local array we can hack on. 100 SmallVector<CharSourceRange, 20> MutableRanges(Ranges); 101 102 SmallVector<FixItHint, 8> MergedFixits; 103 if (!FixItHints.empty()) { 104 mergeFixits(FixItHints, Loc.getManager(), LangOpts, MergedFixits); 105 FixItHints = MergedFixits; 106 } 107 108 for (const auto &Hint : FixItHints) 109 if (Hint.RemoveRange.isValid()) 110 MutableRanges.push_back(Hint.RemoveRange); 111 112 FullSourceLoc UnexpandedLoc = Loc; 113 114 // Find the ultimate expansion location for the diagnostic. 115 Loc = Loc.getFileLoc(); 116 117 PresumedLoc PLoc = Loc.getPresumedLoc(DiagOpts.ShowPresumedLoc); 118 119 // First, if this diagnostic is not in the main file, print out the 120 // "included from" lines. 121 emitIncludeStack(Loc, PLoc, Level); 122 123 // Next, emit the actual diagnostic message and caret. 124 emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, D); 125 emitCaret(Loc, Level, MutableRanges, FixItHints); 126 127 // If this location is within a macro, walk from UnexpandedLoc up to Loc 128 // and produce a macro backtrace. 129 if (UnexpandedLoc.isValid() && UnexpandedLoc.isMacroID()) { 130 emitMacroExpansions(UnexpandedLoc, Level, MutableRanges, FixItHints); 131 } 132 } 133 134 LastLoc = Loc; 135 LastLevel = Level; 136 137 endDiagnostic(D, Level); 138 } 139 140 void DiagnosticRenderer::emitStoredDiagnostic(StoredDiagnostic &Diag) { 141 emitDiagnostic(Diag.getLocation(), Diag.getLevel(), Diag.getMessage(), 142 Diag.getRanges(), Diag.getFixIts(), 143 &Diag); 144 } 145 146 void DiagnosticRenderer::emitBasicNote(StringRef Message) { 147 emitDiagnosticMessage(FullSourceLoc(), PresumedLoc(), DiagnosticsEngine::Note, 148 Message, {}, DiagOrStoredDiag()); 149 } 150 151 /// Prints an include stack when appropriate for a particular 152 /// diagnostic level and location. 153 /// 154 /// This routine handles all the logic of suppressing particular include 155 /// stacks (such as those for notes) and duplicate include stacks when 156 /// repeated warnings occur within the same file. It also handles the logic 157 /// of customizing the formatting and display of the include stack. 158 /// 159 /// \param Loc The diagnostic location. 160 /// \param PLoc The presumed location of the diagnostic location. 161 /// \param Level The diagnostic level of the message this stack pertains to. 162 void DiagnosticRenderer::emitIncludeStack(FullSourceLoc Loc, PresumedLoc PLoc, 163 DiagnosticsEngine::Level Level) { 164 FullSourceLoc IncludeLoc = 165 PLoc.isInvalid() ? FullSourceLoc() 166 : FullSourceLoc(PLoc.getIncludeLoc(), Loc.getManager()); 167 168 // Skip redundant include stacks altogether. 169 if (LastIncludeLoc == IncludeLoc) 170 return; 171 172 LastIncludeLoc = IncludeLoc; 173 174 if (!DiagOpts.ShowNoteIncludeStack && Level == DiagnosticsEngine::Note) 175 return; 176 177 if (IncludeLoc.isValid()) 178 emitIncludeStackRecursively(IncludeLoc); 179 else { 180 emitModuleBuildStack(Loc.getManager()); 181 emitImportStack(Loc); 182 } 183 } 184 185 /// Helper to recursively walk up the include stack and print each layer 186 /// on the way back down. 187 void DiagnosticRenderer::emitIncludeStackRecursively(FullSourceLoc Loc) { 188 if (Loc.isInvalid()) { 189 emitModuleBuildStack(Loc.getManager()); 190 return; 191 } 192 193 PresumedLoc PLoc = Loc.getPresumedLoc(DiagOpts.ShowPresumedLoc); 194 if (PLoc.isInvalid()) 195 return; 196 197 // If this source location was imported from a module, print the module 198 // import stack rather than the 199 // FIXME: We want submodule granularity here. 200 std::pair<FullSourceLoc, StringRef> Imported = Loc.getModuleImportLoc(); 201 if (!Imported.second.empty()) { 202 // This location was imported by a module. Emit the module import stack. 203 emitImportStackRecursively(Imported.first, Imported.second); 204 return; 205 } 206 207 // Emit the other include frames first. 208 emitIncludeStackRecursively( 209 FullSourceLoc(PLoc.getIncludeLoc(), Loc.getManager())); 210 211 // Emit the inclusion text/note. 212 emitIncludeLocation(Loc, PLoc); 213 } 214 215 /// Emit the module import stack associated with the current location. 216 void DiagnosticRenderer::emitImportStack(FullSourceLoc Loc) { 217 if (Loc.isInvalid()) { 218 emitModuleBuildStack(Loc.getManager()); 219 return; 220 } 221 222 std::pair<FullSourceLoc, StringRef> NextImportLoc = Loc.getModuleImportLoc(); 223 emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second); 224 } 225 226 /// Helper to recursively walk up the import stack and print each layer 227 /// on the way back down. 228 void DiagnosticRenderer::emitImportStackRecursively(FullSourceLoc Loc, 229 StringRef ModuleName) { 230 if (ModuleName.empty()) { 231 return; 232 } 233 234 PresumedLoc PLoc = Loc.getPresumedLoc(DiagOpts.ShowPresumedLoc); 235 236 // Emit the other import frames first. 237 std::pair<FullSourceLoc, StringRef> NextImportLoc = Loc.getModuleImportLoc(); 238 emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second); 239 240 // Emit the inclusion text/note. 241 emitImportLocation(Loc, PLoc, ModuleName); 242 } 243 244 /// Emit the module build stack, for cases where a module is (re-)built 245 /// on demand. 246 void DiagnosticRenderer::emitModuleBuildStack(const SourceManager &SM) { 247 ModuleBuildStack Stack = SM.getModuleBuildStack(); 248 for (const auto &I : Stack) { 249 emitBuildingModuleLocation( 250 I.second, I.second.getPresumedLoc(DiagOpts.ShowPresumedLoc), I.first); 251 } 252 } 253 254 /// A recursive function to trace all possible backtrace locations 255 /// to match the \p CaretLocFileID. 256 static SourceLocation 257 retrieveMacroLocation(SourceLocation Loc, FileID MacroFileID, 258 FileID CaretFileID, 259 const SmallVectorImpl<FileID> &CommonArgExpansions, 260 bool IsBegin, const SourceManager *SM, 261 bool &IsTokenRange) { 262 assert(SM->getFileID(Loc) == MacroFileID); 263 if (MacroFileID == CaretFileID) 264 return Loc; 265 if (!Loc.isMacroID()) 266 return {}; 267 268 CharSourceRange MacroRange, MacroArgRange; 269 270 if (SM->isMacroArgExpansion(Loc)) { 271 // Only look at the immediate spelling location of this macro argument if 272 // the other location in the source range is also present in that expansion. 273 if (llvm::binary_search(CommonArgExpansions, MacroFileID)) 274 MacroRange = 275 CharSourceRange(SM->getImmediateSpellingLoc(Loc), IsTokenRange); 276 MacroArgRange = SM->getImmediateExpansionRange(Loc); 277 } else { 278 MacroRange = SM->getImmediateExpansionRange(Loc); 279 MacroArgRange = 280 CharSourceRange(SM->getImmediateSpellingLoc(Loc), IsTokenRange); 281 } 282 283 SourceLocation MacroLocation = 284 IsBegin ? MacroRange.getBegin() : MacroRange.getEnd(); 285 if (MacroLocation.isValid()) { 286 MacroFileID = SM->getFileID(MacroLocation); 287 bool TokenRange = IsBegin ? IsTokenRange : MacroRange.isTokenRange(); 288 MacroLocation = 289 retrieveMacroLocation(MacroLocation, MacroFileID, CaretFileID, 290 CommonArgExpansions, IsBegin, SM, TokenRange); 291 if (MacroLocation.isValid()) { 292 IsTokenRange = TokenRange; 293 return MacroLocation; 294 } 295 } 296 297 // If we moved the end of the range to an expansion location, we now have 298 // a range of the same kind as the expansion range. 299 if (!IsBegin) 300 IsTokenRange = MacroArgRange.isTokenRange(); 301 302 SourceLocation MacroArgLocation = 303 IsBegin ? MacroArgRange.getBegin() : MacroArgRange.getEnd(); 304 MacroFileID = SM->getFileID(MacroArgLocation); 305 return retrieveMacroLocation(MacroArgLocation, MacroFileID, CaretFileID, 306 CommonArgExpansions, IsBegin, SM, IsTokenRange); 307 } 308 309 /// Walk up the chain of macro expansions and collect the FileIDs identifying the 310 /// expansions. 311 static void getMacroArgExpansionFileIDs(SourceLocation Loc, 312 SmallVectorImpl<FileID> &IDs, 313 bool IsBegin, const SourceManager *SM) { 314 while (Loc.isMacroID()) { 315 if (SM->isMacroArgExpansion(Loc)) { 316 IDs.push_back(SM->getFileID(Loc)); 317 Loc = SM->getImmediateSpellingLoc(Loc); 318 } else { 319 auto ExpRange = SM->getImmediateExpansionRange(Loc); 320 Loc = IsBegin ? ExpRange.getBegin() : ExpRange.getEnd(); 321 } 322 } 323 } 324 325 /// Collect the expansions of the begin and end locations and compute the set 326 /// intersection. Produces a sorted vector of FileIDs in CommonArgExpansions. 327 static void computeCommonMacroArgExpansionFileIDs( 328 SourceLocation Begin, SourceLocation End, const SourceManager *SM, 329 SmallVectorImpl<FileID> &CommonArgExpansions) { 330 SmallVector<FileID, 4> BeginArgExpansions; 331 SmallVector<FileID, 4> EndArgExpansions; 332 getMacroArgExpansionFileIDs(Begin, BeginArgExpansions, /*IsBegin=*/true, SM); 333 getMacroArgExpansionFileIDs(End, EndArgExpansions, /*IsBegin=*/false, SM); 334 llvm::sort(BeginArgExpansions); 335 llvm::sort(EndArgExpansions); 336 std::set_intersection(BeginArgExpansions.begin(), BeginArgExpansions.end(), 337 EndArgExpansions.begin(), EndArgExpansions.end(), 338 std::back_inserter(CommonArgExpansions)); 339 } 340 341 // Helper function to fix up source ranges. It takes in an array of ranges, 342 // and outputs an array of ranges where we want to draw the range highlighting 343 // around the location specified by CaretLoc. 344 // 345 // To find locations which correspond to the caret, we crawl the macro caller 346 // chain for the beginning and end of each range. If the caret location 347 // is in a macro expansion, we search each chain for a location 348 // in the same expansion as the caret; otherwise, we crawl to the top of 349 // each chain. Two locations are part of the same macro expansion 350 // iff the FileID is the same. 351 static void 352 mapDiagnosticRanges(FullSourceLoc CaretLoc, ArrayRef<CharSourceRange> Ranges, 353 SmallVectorImpl<CharSourceRange> &SpellingRanges) { 354 FileID CaretLocFileID = CaretLoc.getFileID(); 355 356 const SourceManager *SM = &CaretLoc.getManager(); 357 358 for (const auto &Range : Ranges) { 359 if (Range.isInvalid()) 360 continue; 361 362 SourceLocation Begin = Range.getBegin(), End = Range.getEnd(); 363 bool IsTokenRange = Range.isTokenRange(); 364 365 FileID BeginFileID = SM->getFileID(Begin); 366 FileID EndFileID = SM->getFileID(End); 367 368 // Find the common parent for the beginning and end of the range. 369 370 // First, crawl the expansion chain for the beginning of the range. 371 llvm::SmallDenseMap<FileID, SourceLocation> BeginLocsMap; 372 while (Begin.isMacroID() && BeginFileID != EndFileID) { 373 BeginLocsMap[BeginFileID] = Begin; 374 Begin = SM->getImmediateExpansionRange(Begin).getBegin(); 375 BeginFileID = SM->getFileID(Begin); 376 } 377 378 // Then, crawl the expansion chain for the end of the range. 379 if (BeginFileID != EndFileID) { 380 while (End.isMacroID() && !BeginLocsMap.count(EndFileID)) { 381 auto Exp = SM->getImmediateExpansionRange(End); 382 IsTokenRange = Exp.isTokenRange(); 383 End = Exp.getEnd(); 384 EndFileID = SM->getFileID(End); 385 } 386 if (End.isMacroID()) { 387 Begin = BeginLocsMap[EndFileID]; 388 BeginFileID = EndFileID; 389 } 390 } 391 392 // There is a chance that begin or end is invalid here, for example if 393 // specific compile error is reported. 394 // It is possible that the FileID's do not match, if one comes from an 395 // included file. In this case we can not produce a meaningful source range. 396 if (Begin.isInvalid() || End.isInvalid() || BeginFileID != EndFileID) 397 continue; 398 399 // Do the backtracking. 400 SmallVector<FileID, 4> CommonArgExpansions; 401 computeCommonMacroArgExpansionFileIDs(Begin, End, SM, CommonArgExpansions); 402 Begin = retrieveMacroLocation(Begin, BeginFileID, CaretLocFileID, 403 CommonArgExpansions, /*IsBegin=*/true, SM, 404 IsTokenRange); 405 End = retrieveMacroLocation(End, BeginFileID, CaretLocFileID, 406 CommonArgExpansions, /*IsBegin=*/false, SM, 407 IsTokenRange); 408 if (Begin.isInvalid() || End.isInvalid()) continue; 409 410 // Return the spelling location of the beginning and end of the range. 411 Begin = SM->getSpellingLoc(Begin); 412 End = SM->getSpellingLoc(End); 413 414 SpellingRanges.push_back(CharSourceRange(SourceRange(Begin, End), 415 IsTokenRange)); 416 } 417 } 418 419 void DiagnosticRenderer::emitCaret(FullSourceLoc Loc, 420 DiagnosticsEngine::Level Level, 421 ArrayRef<CharSourceRange> Ranges, 422 ArrayRef<FixItHint> Hints) { 423 SmallVector<CharSourceRange, 4> SpellingRanges; 424 mapDiagnosticRanges(Loc, Ranges, SpellingRanges); 425 emitCodeContext(Loc, Level, SpellingRanges, Hints); 426 } 427 428 /// A helper function for emitMacroExpansion to print the 429 /// macro expansion message 430 void DiagnosticRenderer::emitSingleMacroExpansion( 431 FullSourceLoc Loc, DiagnosticsEngine::Level Level, 432 ArrayRef<CharSourceRange> Ranges) { 433 // Find the spelling location for the macro definition. We must use the 434 // spelling location here to avoid emitting a macro backtrace for the note. 435 FullSourceLoc SpellingLoc = Loc.getSpellingLoc(); 436 437 // Map the ranges into the FileID of the diagnostic location. 438 SmallVector<CharSourceRange, 4> SpellingRanges; 439 mapDiagnosticRanges(Loc, Ranges, SpellingRanges); 440 441 SmallString<100> MessageStorage; 442 llvm::raw_svector_ostream Message(MessageStorage); 443 StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics( 444 Loc, Loc.getManager(), LangOpts); 445 if (MacroName.empty()) 446 Message << "expanded from here"; 447 else 448 Message << "expanded from macro '" << MacroName << "'"; 449 450 emitDiagnostic(SpellingLoc, DiagnosticsEngine::Note, Message.str(), 451 SpellingRanges, {}); 452 } 453 454 /// A helper function to check if the current ranges are all inside the same 455 /// macro argument expansion as Loc. 456 static bool 457 rangesInsideSameMacroArgExpansion(FullSourceLoc Loc, 458 ArrayRef<CharSourceRange> Ranges) { 459 assert(Loc.isMacroID() && "Must be a macro expansion!"); 460 461 SmallVector<CharSourceRange> SpellingRanges; 462 mapDiagnosticRanges(Loc, Ranges, SpellingRanges); 463 464 unsigned ValidCount = 465 llvm::count_if(Ranges, [](const auto &R) { return R.isValid(); }); 466 if (ValidCount > SpellingRanges.size()) 467 return false; 468 469 const SourceManager &SM = Loc.getManager(); 470 for (const auto &R : Ranges) { 471 // All positions in the range need to point to Loc. 472 SourceLocation Begin = R.getBegin(); 473 if (Begin == R.getEnd()) { 474 if (!SM.isMacroArgExpansion(Begin)) 475 return false; 476 continue; 477 } 478 479 while (Begin != R.getEnd()) { 480 SourceLocation MacroLoc; 481 if (!SM.isMacroArgExpansion(Begin, &MacroLoc)) 482 return false; 483 if (MacroLoc != Loc) 484 return false; 485 486 Begin = Begin.getLocWithOffset(1); 487 } 488 } 489 490 return true; 491 } 492 493 /// Recursively emit notes for each macro expansion and caret 494 /// diagnostics where appropriate. 495 /// 496 /// Walks up the macro expansion stack printing expansion notes, the code 497 /// snippet, caret, underlines and FixItHint display as appropriate at each 498 /// level. 499 /// 500 /// \param Loc The location for this caret. 501 /// \param Level The diagnostic level currently being emitted. 502 /// \param Ranges The underlined ranges for this code snippet. 503 /// \param Hints The FixIt hints active for this diagnostic. 504 void DiagnosticRenderer::emitMacroExpansions(FullSourceLoc Loc, 505 DiagnosticsEngine::Level Level, 506 ArrayRef<CharSourceRange> Ranges, 507 ArrayRef<FixItHint> Hints) { 508 assert(Loc.isValid() && "must have a valid source location here"); 509 const SourceManager &SM = Loc.getManager(); 510 SourceLocation L = Loc; 511 512 // Produce a stack of macro backtraces. 513 SmallVector<SourceLocation, 8> LocationStack; 514 unsigned IgnoredEnd = 0; 515 while (L.isMacroID()) { 516 // If this is the expansion of a macro argument, point the caret at the 517 // use of the argument in the definition of the macro, not the expansion. 518 if (SM.isMacroArgExpansion(L)) { 519 LocationStack.push_back(SM.getImmediateExpansionRange(L).getBegin()); 520 521 if (rangesInsideSameMacroArgExpansion(FullSourceLoc(L, SM), Ranges)) 522 IgnoredEnd = LocationStack.size(); 523 } else 524 LocationStack.push_back(L); 525 526 L = SM.getImmediateMacroCallerLoc(L); 527 528 // Once the location no longer points into a macro, try stepping through 529 // the last found location. This sometimes produces additional useful 530 // backtraces. 531 if (L.isFileID()) 532 L = SM.getImmediateMacroCallerLoc(LocationStack.back()); 533 assert(L.isValid() && "must have a valid source location here"); 534 } 535 536 LocationStack.erase(LocationStack.begin(), 537 LocationStack.begin() + IgnoredEnd); 538 539 unsigned MacroDepth = LocationStack.size(); 540 unsigned MacroLimit = DiagOpts.MacroBacktraceLimit; 541 if (MacroDepth <= MacroLimit || MacroLimit == 0) { 542 for (auto I = LocationStack.rbegin(), E = LocationStack.rend(); 543 I != E; ++I) 544 emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges); 545 return; 546 } 547 548 unsigned MacroStartMessages = MacroLimit / 2; 549 unsigned MacroEndMessages = MacroLimit / 2 + MacroLimit % 2; 550 551 for (auto I = LocationStack.rbegin(), 552 E = LocationStack.rbegin() + MacroStartMessages; 553 I != E; ++I) 554 emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges); 555 556 SmallString<200> MessageStorage; 557 llvm::raw_svector_ostream Message(MessageStorage); 558 Message << "(skipping " << (MacroDepth - MacroLimit) 559 << " expansions in backtrace; use -fmacro-backtrace-limit=0 to " 560 "see all)"; 561 emitBasicNote(Message.str()); 562 563 for (auto I = LocationStack.rend() - MacroEndMessages, 564 E = LocationStack.rend(); 565 I != E; ++I) 566 emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges); 567 } 568 569 DiagnosticNoteRenderer::~DiagnosticNoteRenderer() = default; 570 571 void DiagnosticNoteRenderer::emitIncludeLocation(FullSourceLoc Loc, 572 PresumedLoc PLoc) { 573 // Generate a note indicating the include location. 574 SmallString<200> MessageStorage; 575 llvm::raw_svector_ostream Message(MessageStorage); 576 Message << "in file included from " << PLoc.getFilename() << ':' 577 << PLoc.getLine() << ":"; 578 emitNote(Loc, Message.str()); 579 } 580 581 void DiagnosticNoteRenderer::emitImportLocation(FullSourceLoc Loc, 582 PresumedLoc PLoc, 583 StringRef ModuleName) { 584 // Generate a note indicating the include location. 585 SmallString<200> MessageStorage; 586 llvm::raw_svector_ostream Message(MessageStorage); 587 Message << "in module '" << ModuleName; 588 if (PLoc.isValid()) 589 Message << "' imported from " << PLoc.getFilename() << ':' 590 << PLoc.getLine(); 591 Message << ":"; 592 emitNote(Loc, Message.str()); 593 } 594 595 void DiagnosticNoteRenderer::emitBuildingModuleLocation(FullSourceLoc Loc, 596 PresumedLoc PLoc, 597 StringRef ModuleName) { 598 // Generate a note indicating the include location. 599 SmallString<200> MessageStorage; 600 llvm::raw_svector_ostream Message(MessageStorage); 601 if (PLoc.isValid()) 602 Message << "while building module '" << ModuleName << "' imported from " 603 << PLoc.getFilename() << ':' << PLoc.getLine() << ":"; 604 else 605 Message << "while building module '" << ModuleName << "':"; 606 emitNote(Loc, Message.str()); 607 } 608