DiagnosticRenderer.cpp revision 0fdcd1e81db7b8f5b3fd05154d966b215b20a2eb
1//===--- DiagnosticRenderer.cpp - Diagnostic Pretty-Printing --------------===// 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#include "clang/Frontend/DiagnosticRenderer.h" 11#include "clang/Basic/DiagnosticOptions.h" 12#include "clang/Basic/FileManager.h" 13#include "clang/Basic/SourceManager.h" 14#include "clang/Edit/Commit.h" 15#include "clang/Edit/EditedSource.h" 16#include "clang/Edit/EditsReceiver.h" 17#include "clang/Lex/Lexer.h" 18#include "llvm/ADT/SmallSet.h" 19#include "llvm/ADT/SmallString.h" 20#include "llvm/Support/ErrorHandling.h" 21#include "llvm/Support/MemoryBuffer.h" 22#include "llvm/Support/raw_ostream.h" 23#include <algorithm> 24using namespace clang; 25 26/// \brief Retrieve the name of the immediate macro expansion. 27/// 28/// This routine starts from a source location, and finds the name of the macro 29/// responsible for its immediate expansion. It looks through any intervening 30/// macro argument expansions to compute this. It returns a StringRef which 31/// refers to the SourceManager-owned buffer of the source where that macro 32/// name is spelled. Thus, the result shouldn't out-live that SourceManager. 33/// 34/// This differs from Lexer::getImmediateMacroName in that any macro argument 35/// location will result in the topmost function macro that accepted it. 36/// e.g. 37/// \code 38/// MAC1( MAC2(foo) ) 39/// \endcode 40/// for location of 'foo' token, this function will return "MAC1" while 41/// Lexer::getImmediateMacroName will return "MAC2". 42static StringRef getImmediateMacroName(SourceLocation Loc, 43 const SourceManager &SM, 44 const LangOptions &LangOpts) { 45 assert(Loc.isMacroID() && "Only reasonble to call this on macros"); 46 // Walk past macro argument expanions. 47 while (SM.isMacroArgExpansion(Loc)) 48 Loc = SM.getImmediateExpansionRange(Loc).first; 49 50 // If the macro's spelling has no FileID, then it's actually a token paste 51 // or stringization (or similar) and not a macro at all. 52 if (!SM.getFileEntryForID(SM.getFileID(SM.getSpellingLoc(Loc)))) 53 return StringRef(); 54 55 // Find the spelling location of the start of the non-argument expansion 56 // range. This is where the macro name was spelled in order to begin 57 // expanding this macro. 58 Loc = SM.getSpellingLoc(SM.getImmediateExpansionRange(Loc).first); 59 60 // Dig out the buffer where the macro name was spelled and the extents of the 61 // name so that we can render it into the expansion note. 62 std::pair<FileID, unsigned> ExpansionInfo = SM.getDecomposedLoc(Loc); 63 unsigned MacroTokenLength = Lexer::MeasureTokenLength(Loc, SM, LangOpts); 64 StringRef ExpansionBuffer = SM.getBufferData(ExpansionInfo.first); 65 return ExpansionBuffer.substr(ExpansionInfo.second, MacroTokenLength); 66} 67 68DiagnosticRenderer::DiagnosticRenderer(const LangOptions &LangOpts, 69 DiagnosticOptions *DiagOpts) 70 : LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {} 71 72DiagnosticRenderer::~DiagnosticRenderer() {} 73 74namespace { 75 76class FixitReceiver : public edit::EditsReceiver { 77 SmallVectorImpl<FixItHint> &MergedFixits; 78 79public: 80 FixitReceiver(SmallVectorImpl<FixItHint> &MergedFixits) 81 : MergedFixits(MergedFixits) { } 82 virtual void insert(SourceLocation loc, StringRef text) { 83 MergedFixits.push_back(FixItHint::CreateInsertion(loc, text)); 84 } 85 virtual void replace(CharSourceRange range, StringRef text) { 86 MergedFixits.push_back(FixItHint::CreateReplacement(range, text)); 87 } 88}; 89 90} 91 92static void mergeFixits(ArrayRef<FixItHint> FixItHints, 93 const SourceManager &SM, const LangOptions &LangOpts, 94 SmallVectorImpl<FixItHint> &MergedFixits) { 95 edit::Commit commit(SM, LangOpts); 96 for (ArrayRef<FixItHint>::const_iterator 97 I = FixItHints.begin(), E = FixItHints.end(); I != E; ++I) { 98 const FixItHint &Hint = *I; 99 if (Hint.CodeToInsert.empty()) { 100 if (Hint.InsertFromRange.isValid()) 101 commit.insertFromRange(Hint.RemoveRange.getBegin(), 102 Hint.InsertFromRange, /*afterToken=*/false, 103 Hint.BeforePreviousInsertions); 104 else 105 commit.remove(Hint.RemoveRange); 106 } else { 107 if (Hint.RemoveRange.isTokenRange() || 108 Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd()) 109 commit.replace(Hint.RemoveRange, Hint.CodeToInsert); 110 else 111 commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert, 112 /*afterToken=*/false, Hint.BeforePreviousInsertions); 113 } 114 } 115 116 edit::EditedSource Editor(SM, LangOpts); 117 if (Editor.commit(commit)) { 118 FixitReceiver Rec(MergedFixits); 119 Editor.applyRewrites(Rec); 120 } 121} 122 123void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc, 124 DiagnosticsEngine::Level Level, 125 StringRef Message, 126 ArrayRef<CharSourceRange> Ranges, 127 ArrayRef<FixItHint> FixItHints, 128 const SourceManager *SM, 129 DiagOrStoredDiag D) { 130 assert(SM || Loc.isInvalid()); 131 132 beginDiagnostic(D, Level); 133 134 if (!Loc.isValid()) 135 // If we have no source location, just emit the diagnostic message. 136 emitDiagnosticMessage(Loc, PresumedLoc(), Level, Message, Ranges, SM, D); 137 else { 138 // Get the ranges into a local array we can hack on. 139 SmallVector<CharSourceRange, 20> MutableRanges(Ranges.begin(), 140 Ranges.end()); 141 142 llvm::SmallVector<FixItHint, 8> MergedFixits; 143 if (!FixItHints.empty()) { 144 mergeFixits(FixItHints, *SM, LangOpts, MergedFixits); 145 FixItHints = MergedFixits; 146 } 147 148 for (ArrayRef<FixItHint>::const_iterator I = FixItHints.begin(), 149 E = FixItHints.end(); 150 I != E; ++I) 151 if (I->RemoveRange.isValid()) 152 MutableRanges.push_back(I->RemoveRange); 153 154 SourceLocation UnexpandedLoc = Loc; 155 156 // Perform the same walk as emitMacroExpansions, to find the ultimate 157 // expansion location for the diagnostic. 158 while (Loc.isMacroID()) 159 Loc = SM->getImmediateMacroCallerLoc(Loc); 160 161 PresumedLoc PLoc = SM->getPresumedLoc(Loc, DiagOpts->ShowPresumedLoc); 162 163 // First, if this diagnostic is not in the main file, print out the 164 // "included from" lines. 165 emitIncludeStack(Loc, PLoc, Level, *SM); 166 167 // Next, emit the actual diagnostic message and caret. 168 emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, SM, D); 169 emitCaret(Loc, Level, MutableRanges, FixItHints, *SM); 170 171 // If this location is within a macro, walk from UnexpandedLoc up to Loc 172 // and produce a macro backtrace. 173 if (UnexpandedLoc.isValid() && UnexpandedLoc.isMacroID()) { 174 unsigned MacroDepth = 0; 175 emitMacroExpansions(UnexpandedLoc, Level, MutableRanges, FixItHints, *SM, 176 MacroDepth); 177 } 178 } 179 180 LastLoc = Loc; 181 LastLevel = Level; 182 183 endDiagnostic(D, Level); 184} 185 186 187void DiagnosticRenderer::emitStoredDiagnostic(StoredDiagnostic &Diag) { 188 emitDiagnostic(Diag.getLocation(), Diag.getLevel(), Diag.getMessage(), 189 Diag.getRanges(), Diag.getFixIts(), 190 Diag.getLocation().isValid() ? &Diag.getLocation().getManager() 191 : 0, 192 &Diag); 193} 194 195/// \brief Prints an include stack when appropriate for a particular 196/// diagnostic level and location. 197/// 198/// This routine handles all the logic of suppressing particular include 199/// stacks (such as those for notes) and duplicate include stacks when 200/// repeated warnings occur within the same file. It also handles the logic 201/// of customizing the formatting and display of the include stack. 202/// 203/// \param Loc The diagnostic location. 204/// \param PLoc The presumed location of the diagnostic location. 205/// \param Level The diagnostic level of the message this stack pertains to. 206void DiagnosticRenderer::emitIncludeStack(SourceLocation Loc, 207 PresumedLoc PLoc, 208 DiagnosticsEngine::Level Level, 209 const SourceManager &SM) { 210 SourceLocation IncludeLoc = PLoc.getIncludeLoc(); 211 212 // Skip redundant include stacks altogether. 213 if (LastIncludeLoc == IncludeLoc) 214 return; 215 216 LastIncludeLoc = IncludeLoc; 217 218 if (!DiagOpts->ShowNoteIncludeStack && Level == DiagnosticsEngine::Note) 219 return; 220 221 if (IncludeLoc.isValid()) 222 emitIncludeStackRecursively(IncludeLoc, SM); 223 else { 224 emitModuleBuildStack(SM); 225 emitImportStack(Loc, SM); 226 } 227} 228 229/// \brief Helper to recursivly walk up the include stack and print each layer 230/// on the way back down. 231void DiagnosticRenderer::emitIncludeStackRecursively(SourceLocation Loc, 232 const SourceManager &SM) { 233 if (Loc.isInvalid()) { 234 emitModuleBuildStack(SM); 235 return; 236 } 237 238 PresumedLoc PLoc = SM.getPresumedLoc(Loc, DiagOpts->ShowPresumedLoc); 239 if (PLoc.isInvalid()) 240 return; 241 242 // If this source location was imported from a module, print the module 243 // import stack rather than the 244 // FIXME: We want submodule granularity here. 245 std::pair<SourceLocation, StringRef> Imported = SM.getModuleImportLoc(Loc); 246 if (Imported.first.isValid()) { 247 // This location was imported by a module. Emit the module import stack. 248 emitImportStackRecursively(Imported.first, Imported.second, SM); 249 return; 250 } 251 252 // Emit the other include frames first. 253 emitIncludeStackRecursively(PLoc.getIncludeLoc(), SM); 254 255 // Emit the inclusion text/note. 256 emitIncludeLocation(Loc, PLoc, SM); 257} 258 259/// \brief Emit the module import stack associated with the current location. 260void DiagnosticRenderer::emitImportStack(SourceLocation Loc, 261 const SourceManager &SM) { 262 if (Loc.isInvalid()) { 263 emitModuleBuildStack(SM); 264 return; 265 } 266 267 std::pair<SourceLocation, StringRef> NextImportLoc 268 = SM.getModuleImportLoc(Loc); 269 emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second, SM); 270} 271 272/// \brief Helper to recursivly walk up the import stack and print each layer 273/// on the way back down. 274void DiagnosticRenderer::emitImportStackRecursively(SourceLocation Loc, 275 StringRef ModuleName, 276 const SourceManager &SM) { 277 if (Loc.isInvalid()) { 278 return; 279 } 280 281 PresumedLoc PLoc = SM.getPresumedLoc(Loc, DiagOpts->ShowPresumedLoc); 282 if (PLoc.isInvalid()) 283 return; 284 285 // Emit the other import frames first. 286 std::pair<SourceLocation, StringRef> NextImportLoc 287 = SM.getModuleImportLoc(Loc); 288 emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second, SM); 289 290 // Emit the inclusion text/note. 291 emitImportLocation(Loc, PLoc, ModuleName, SM); 292} 293 294/// \brief Emit the module build stack, for cases where a module is (re-)built 295/// on demand. 296void DiagnosticRenderer::emitModuleBuildStack(const SourceManager &SM) { 297 ModuleBuildStack Stack = SM.getModuleBuildStack(); 298 for (unsigned I = 0, N = Stack.size(); I != N; ++I) { 299 const SourceManager &CurSM = Stack[I].second.getManager(); 300 SourceLocation CurLoc = Stack[I].second; 301 emitBuildingModuleLocation(CurLoc, 302 CurSM.getPresumedLoc(CurLoc, 303 DiagOpts->ShowPresumedLoc), 304 Stack[I].first, 305 CurSM); 306 } 307} 308 309// Find the common parent for the beginning and end of the range. 310static void findCommonParent(SourceLocation &Begin, SourceLocation &End, 311 const SourceManager *SM) { 312 if (Begin.isInvalid() || End.isInvalid()) { 313 Begin = End = SourceLocation(); 314 return; 315 } 316 317 FileID BeginFileID = SM->getFileID(Begin); 318 FileID EndFileID = SM->getFileID(End); 319 320 // First, crawl the expansion chain for the beginning of the range. 321 llvm::SmallDenseMap<FileID, SourceLocation> BeginLocsMap; 322 BeginLocsMap[BeginFileID] = Begin; 323 while (Begin.isMacroID() && BeginFileID != EndFileID) { 324 if (SM->isMacroArgExpansion(Begin)) { 325 Begin = SM->getImmediateSpellingLoc(Begin); 326 if (Begin.isMacroID()) 327 continue; 328 Begin = SourceLocation(); 329 BeginFileID = FileID(); 330 break; 331 } 332 Begin = SM->getImmediateExpansionRange(Begin).first; 333 BeginFileID = SM->getFileID(Begin); 334 BeginLocsMap[BeginFileID] = Begin; 335 } 336 337 // Then, crawl the expansion chain for the end of the range. 338 if (BeginFileID != EndFileID) { 339 while (End.isMacroID() && !BeginLocsMap.count(EndFileID)) { 340 if (SM->isMacroArgExpansion(End)) { 341 End = SM->getImmediateSpellingLoc(End); 342 if (End.isMacroID()) 343 continue; 344 End = SourceLocation(); 345 EndFileID = FileID(); 346 break; 347 } 348 End = SM->getImmediateExpansionRange(End).second; 349 EndFileID = SM->getFileID(End); 350 } 351 if (End.isMacroID()) { 352 Begin = BeginLocsMap[EndFileID]; 353 BeginFileID = EndFileID; 354 } 355 } 356 assert(Begin.isValid() == End.isValid()); 357} 358 359// Helper function to fix up source ranges. It takes in an array of ranges, 360// and outputs an array of ranges where we want to draw the range highlighting 361// around the location specified by CaretLoc. 362// 363// To find locations which correspond to the caret, we crawl the macro caller 364// chain for the beginning and end of each range. If the caret location 365// is in a macro expansion, we search each chain for a location 366// in the same expansion as the caret; otherwise, we crawl to the top of 367// each chain. Two locations are part of the same macro expansion 368// iff the FileID is the same. 369static void mapDiagnosticRanges( 370 SourceLocation CaretLoc, 371 ArrayRef<CharSourceRange> Ranges, 372 SmallVectorImpl<CharSourceRange> &SpellingRanges, 373 const SourceManager *SM) { 374 FileID CaretLocFileID = SM->getFileID(CaretLoc); 375 376 for (ArrayRef<CharSourceRange>::const_iterator I = Ranges.begin(), 377 E = Ranges.end(); 378 I != E; ++I) { 379 SourceLocation Begin = I->getBegin(), End = I->getEnd(); 380 bool IsTokenRange = I->isTokenRange(); 381 382 // Compute the common parent; we can't highlight a range where 383 // the begin and end have different FileIDs. 384 findCommonParent(Begin, End, SM); 385 386 FileID BeginFileID = SM->getFileID(Begin); 387 388 while (Begin.isMacroID() && BeginFileID != CaretLocFileID) { 389 if (SM->isMacroArgExpansion(Begin)) { 390 // We have a macro argument; take the spelling loc, which is 391 // a step closer to where the argument was written. 392 Begin = SM->getImmediateSpellingLoc(Begin); 393 End = SM->getImmediateSpellingLoc(End); 394 BeginFileID = SM->getFileID(Begin); 395 assert(BeginFileID == SM->getFileID(End)); 396 } else { 397 // Take the next expansion in the expansion chain. 398 Begin = SM->getImmediateExpansionRange(Begin).first; 399 End = SM->getImmediateExpansionRange(End).second; 400 401 // Compute the common parent again; the beginning and end might 402 // come out of different macro expansions. 403 findCommonParent(Begin, End, SM); 404 BeginFileID = SM->getFileID(Begin); 405 } 406 } 407 408 // If this is the expansion of a macro argument, point the range at the 409 // use of the argument in the definition of the macro, not the expansion. 410 if (SM->isMacroArgExpansion(Begin)) { 411 assert(SM->isMacroArgExpansion(End)); 412 Begin = End = SM->getImmediateExpansionRange(Begin).first; 413 IsTokenRange = true; 414 } 415 416 // Return the spelling location of the beginning and end of the range. 417 Begin = SM->getSpellingLoc(Begin); 418 End = SM->getSpellingLoc(End); 419 SpellingRanges.push_back(CharSourceRange(SourceRange(Begin, End), 420 IsTokenRange)); 421 } 422} 423 424void DiagnosticRenderer::emitCaret(SourceLocation Loc, 425 DiagnosticsEngine::Level Level, 426 ArrayRef<CharSourceRange> Ranges, 427 ArrayRef<FixItHint> Hints, 428 const SourceManager &SM) { 429 SmallVector<CharSourceRange, 4> SpellingRanges; 430 mapDiagnosticRanges(Loc, Ranges, SpellingRanges, &SM); 431 emitCodeContext(Loc, Level, SpellingRanges, Hints, SM); 432} 433 434/// \brief Recursively emit notes for each macro expansion and caret 435/// diagnostics where appropriate. 436/// 437/// Walks up the macro expansion stack printing expansion notes, the code 438/// snippet, caret, underlines and FixItHint display as appropriate at each 439/// level. 440/// 441/// \param Loc The location for this caret. 442/// \param Level The diagnostic level currently being emitted. 443/// \param Ranges The underlined ranges for this code snippet. 444/// \param Hints The FixIt hints active for this diagnostic. 445/// \param MacroSkipEnd The depth to stop skipping macro expansions. 446/// \param OnMacroInst The current depth of the macro expansion stack. 447void DiagnosticRenderer::emitMacroExpansions(SourceLocation Loc, 448 DiagnosticsEngine::Level Level, 449 ArrayRef<CharSourceRange> Ranges, 450 ArrayRef<FixItHint> Hints, 451 const SourceManager &SM, 452 unsigned &MacroDepth, 453 unsigned OnMacroInst) { 454 assert(!Loc.isInvalid() && "must have a valid source location here"); 455 456 // Walk up to the caller of this macro, and produce a backtrace down to there. 457 SourceLocation OneLevelUp = SM.getImmediateMacroCallerLoc(Loc); 458 if (OneLevelUp.isMacroID()) 459 emitMacroExpansions(OneLevelUp, Level, Ranges, Hints, SM, 460 MacroDepth, OnMacroInst + 1); 461 else 462 MacroDepth = OnMacroInst + 1; 463 464 unsigned MacroSkipStart = 0, MacroSkipEnd = 0; 465 if (MacroDepth > DiagOpts->MacroBacktraceLimit && 466 DiagOpts->MacroBacktraceLimit != 0) { 467 MacroSkipStart = DiagOpts->MacroBacktraceLimit / 2 + 468 DiagOpts->MacroBacktraceLimit % 2; 469 MacroSkipEnd = MacroDepth - DiagOpts->MacroBacktraceLimit / 2; 470 } 471 472 // Whether to suppress printing this macro expansion. 473 bool Suppressed = (OnMacroInst >= MacroSkipStart && 474 OnMacroInst < MacroSkipEnd); 475 476 if (Suppressed) { 477 // Tell the user that we've skipped contexts. 478 if (OnMacroInst == MacroSkipStart) { 479 SmallString<200> MessageStorage; 480 llvm::raw_svector_ostream Message(MessageStorage); 481 Message << "(skipping " << (MacroSkipEnd - MacroSkipStart) 482 << " expansions in backtrace; use -fmacro-backtrace-limit=0 to " 483 "see all)"; 484 emitBasicNote(Message.str()); 485 } 486 return; 487 } 488 489 // Find the spelling location for the macro definition. We must use the 490 // spelling location here to avoid emitting a macro bactrace for the note. 491 SourceLocation SpellingLoc = Loc; 492 // If this is the expansion of a macro argument, point the caret at the 493 // use of the argument in the definition of the macro, not the expansion. 494 if (SM.isMacroArgExpansion(Loc)) 495 SpellingLoc = SM.getImmediateExpansionRange(Loc).first; 496 SpellingLoc = SM.getSpellingLoc(SpellingLoc); 497 498 // Map the ranges into the FileID of the diagnostic location. 499 SmallVector<CharSourceRange, 4> SpellingRanges; 500 mapDiagnosticRanges(Loc, Ranges, SpellingRanges, &SM); 501 502 SmallString<100> MessageStorage; 503 llvm::raw_svector_ostream Message(MessageStorage); 504 StringRef MacroName = getImmediateMacroName(Loc, SM, LangOpts); 505 if (MacroName.empty()) 506 Message << "expanded from here"; 507 else 508 Message << "expanded from macro '" << MacroName << "'"; 509 emitDiagnostic(SpellingLoc, DiagnosticsEngine::Note, 510 Message.str(), 511 SpellingRanges, ArrayRef<FixItHint>(), &SM); 512} 513 514DiagnosticNoteRenderer::~DiagnosticNoteRenderer() {} 515 516void DiagnosticNoteRenderer::emitIncludeLocation(SourceLocation Loc, 517 PresumedLoc PLoc, 518 const SourceManager &SM) { 519 // Generate a note indicating the include location. 520 SmallString<200> MessageStorage; 521 llvm::raw_svector_ostream Message(MessageStorage); 522 Message << "in file included from " << PLoc.getFilename() << ':' 523 << PLoc.getLine() << ":"; 524 emitNote(Loc, Message.str(), &SM); 525} 526 527void DiagnosticNoteRenderer::emitImportLocation(SourceLocation Loc, 528 PresumedLoc PLoc, 529 StringRef ModuleName, 530 const SourceManager &SM) { 531 // Generate a note indicating the include location. 532 SmallString<200> MessageStorage; 533 llvm::raw_svector_ostream Message(MessageStorage); 534 Message << "in module '" << ModuleName << "' imported from " 535 << PLoc.getFilename() << ':' << PLoc.getLine() << ":"; 536 emitNote(Loc, Message.str(), &SM); 537} 538 539void 540DiagnosticNoteRenderer::emitBuildingModuleLocation(SourceLocation Loc, 541 PresumedLoc PLoc, 542 StringRef ModuleName, 543 const SourceManager &SM) { 544 // Generate a note indicating the include location. 545 SmallString<200> MessageStorage; 546 llvm::raw_svector_ostream Message(MessageStorage); 547 Message << "while building module '" << ModuleName << "' imported from " 548 << PLoc.getFilename() << ':' << PLoc.getLine() << ":"; 549 emitNote(Loc, Message.str(), &SM); 550} 551 552 553void DiagnosticNoteRenderer::emitBasicNote(StringRef Message) { 554 emitNote(SourceLocation(), Message, 0); 555} 556