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