//===--- DiagnosticRenderer.cpp - Diagnostic Pretty-Printing --------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "clang/Frontend/DiagnosticRenderer.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Frontend/DiagnosticOptions.h" #include "clang/Lex/Lexer.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/ADT/SmallString.h" #include using namespace clang; /// Look through spelling locations for a macro argument expansion, and /// if found skip to it so that we can trace the argument rather than the macros /// in which that argument is used. If no macro argument expansion is found, /// don't skip anything and return the starting location. static SourceLocation skipToMacroArgExpansion(const SourceManager &SM, SourceLocation StartLoc) { for (SourceLocation L = StartLoc; L.isMacroID(); L = SM.getImmediateSpellingLoc(L)) { if (SM.isMacroArgExpansion(L)) return L; } // Otherwise just return initial location, there's nothing to skip. return StartLoc; } /// Gets the location of the immediate macro caller, one level up the stack /// toward the initial macro typed into the source. static SourceLocation getImmediateMacroCallerLoc(const SourceManager &SM, SourceLocation Loc) { if (!Loc.isMacroID()) return Loc; // When we have the location of (part of) an expanded parameter, its spelling // location points to the argument as typed into the macro call, and // therefore is used to locate the macro caller. if (SM.isMacroArgExpansion(Loc)) return SM.getImmediateSpellingLoc(Loc); // Otherwise, the caller of the macro is located where this macro is // expanded (while the spelling is part of the macro definition). return SM.getImmediateExpansionRange(Loc).first; } /// Gets the location of the immediate macro callee, one level down the stack /// toward the leaf macro. static SourceLocation getImmediateMacroCalleeLoc(const SourceManager &SM, SourceLocation Loc) { if (!Loc.isMacroID()) return Loc; // When we have the location of (part of) an expanded parameter, its // expansion location points to the unexpanded paramater reference within // the macro definition (or callee). if (SM.isMacroArgExpansion(Loc)) return SM.getImmediateExpansionRange(Loc).first; // Otherwise, the callee of the macro is located where this location was // spelled inside the macro definition. return SM.getImmediateSpellingLoc(Loc); } /// Get the presumed location of a diagnostic message. This computes the /// presumed location for the top of any macro backtrace when present. static PresumedLoc getDiagnosticPresumedLoc(const SourceManager &SM, SourceLocation Loc) { // This is a condensed form of the algorithm used by emitCaretDiagnostic to // walk to the top of the macro call stack. while (Loc.isMacroID()) { Loc = skipToMacroArgExpansion(SM, Loc); Loc = getImmediateMacroCallerLoc(SM, Loc); } return SM.getPresumedLoc(Loc); } DiagnosticRenderer::DiagnosticRenderer(const SourceManager &SM, const LangOptions &LangOpts, const DiagnosticOptions &DiagOpts) : SM(SM), LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {} DiagnosticRenderer::~DiagnosticRenderer() {} void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc, DiagnosticsEngine::Level Level, StringRef Message, ArrayRef Ranges, ArrayRef FixItHints, const Diagnostic *Info) { beginDiagnostic(Info, Level); PresumedLoc PLoc = getDiagnosticPresumedLoc(SM, Loc); // First, if this diagnostic is not in the main file, print out the // "included from" lines. emitIncludeStack(PLoc.getIncludeLoc(), Level); // Next, emit the actual diagnostic message. emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, Info); // Only recurse if we have a valid location. if (Loc.isValid()) { // Get the ranges into a local array we can hack on. SmallVector MutableRanges(Ranges.begin(), Ranges.end()); for (ArrayRef::const_iterator I = FixItHints.begin(), E = FixItHints.end(); I != E; ++I) if (I->RemoveRange.isValid()) MutableRanges.push_back(I->RemoveRange); unsigned MacroDepth = 0; emitMacroExpansionsAndCarets(Loc, Level, MutableRanges, FixItHints, MacroDepth); } LastLoc = Loc; LastLevel = Level; endDiagnostic(Info, Level); } /// \brief Prints an include stack when appropriate for a particular /// diagnostic level and location. /// /// This routine handles all the logic of suppressing particular include /// stacks (such as those for notes) and duplicate include stacks when /// repeated warnings occur within the same file. It also handles the logic /// of customizing the formatting and display of the include stack. /// /// \param Level The diagnostic level of the message this stack pertains to. /// \param Loc The include location of the current file (not the diagnostic /// location). void DiagnosticRenderer::emitIncludeStack(SourceLocation Loc, DiagnosticsEngine::Level Level) { // Skip redundant include stacks altogether. if (LastIncludeLoc == Loc) return; LastIncludeLoc = Loc; if (!DiagOpts.ShowNoteIncludeStack && Level == DiagnosticsEngine::Note) return; emitIncludeStackRecursively(Loc); } /// \brief Helper to recursivly walk up the include stack and print each layer /// on the way back down. void DiagnosticRenderer::emitIncludeStackRecursively(SourceLocation Loc) { if (Loc.isInvalid()) return; PresumedLoc PLoc = SM.getPresumedLoc(Loc); if (PLoc.isInvalid()) return; // Emit the other include frames first. emitIncludeStackRecursively(PLoc.getIncludeLoc()); // Emit the inclusion text/note. emitIncludeLocation(Loc, PLoc); } /// \brief Recursively emit notes for each macro expansion and caret /// diagnostics where appropriate. /// /// Walks up the macro expansion stack printing expansion notes, the code /// snippet, caret, underlines and FixItHint display as appropriate at each /// level. /// /// \param Loc The location for this caret. /// \param Level The diagnostic level currently being emitted. /// \param Ranges The underlined ranges for this code snippet. /// \param Hints The FixIt hints active for this diagnostic. /// \param MacroSkipEnd The depth to stop skipping macro expansions. /// \param OnMacroInst The current depth of the macro expansion stack. void DiagnosticRenderer::emitMacroExpansionsAndCarets( SourceLocation Loc, DiagnosticsEngine::Level Level, SmallVectorImpl& Ranges, ArrayRef Hints, unsigned &MacroDepth, unsigned OnMacroInst) { assert(!Loc.isInvalid() && "must have a valid source location here"); // If this is a file source location, directly emit the source snippet and // caret line. Also record the macro depth reached. if (Loc.isFileID()) { assert(MacroDepth == 0 && "We shouldn't hit a leaf node twice!"); MacroDepth = OnMacroInst; emitCodeContext(Loc, Level, Ranges, Hints); return; } // Otherwise recurse through each macro expansion layer. // When processing macros, skip over the expansions leading up to // a macro argument, and trace the argument's expansion stack instead. Loc = skipToMacroArgExpansion(SM, Loc); SourceLocation OneLevelUp = getImmediateMacroCallerLoc(SM, Loc); // FIXME: Map ranges? emitMacroExpansionsAndCarets(OneLevelUp, Level, Ranges, Hints, MacroDepth, OnMacroInst + 1); // Save the original location so we can find the spelling of the macro call. SourceLocation MacroLoc = Loc; // Map the location. Loc = getImmediateMacroCalleeLoc(SM, Loc); unsigned MacroSkipStart = 0, MacroSkipEnd = 0; if (MacroDepth > DiagOpts.MacroBacktraceLimit) { MacroSkipStart = DiagOpts.MacroBacktraceLimit / 2 + DiagOpts.MacroBacktraceLimit % 2; MacroSkipEnd = MacroDepth - DiagOpts.MacroBacktraceLimit / 2; } // Whether to suppress printing this macro expansion. bool Suppressed = (OnMacroInst >= MacroSkipStart && OnMacroInst < MacroSkipEnd); // Map the ranges. for (SmallVectorImpl::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { SourceLocation Start = I->getBegin(), End = I->getEnd(); if (Start.isMacroID()) I->setBegin(getImmediateMacroCalleeLoc(SM, Start)); if (End.isMacroID()) I->setEnd(getImmediateMacroCalleeLoc(SM, End)); } if (Suppressed) { // Tell the user that we've skipped contexts. if (OnMacroInst == MacroSkipStart) { llvm::SmallString<200> MessageStorage; llvm::raw_svector_ostream Message(MessageStorage); Message << "(skipping " << (MacroSkipEnd - MacroSkipStart) << " expansions in backtrace; use -fmacro-backtrace-limit=0 to " "see all)"; emitBasicNote(Message.str()); } return; } llvm::SmallString<100> MessageStorage; llvm::raw_svector_ostream Message(MessageStorage); Message << "expanded from macro '" << Lexer::getImmediateMacroName(MacroLoc, SM, LangOpts) << "'"; emitDiagnostic(SM.getSpellingLoc(Loc), DiagnosticsEngine::Note, Message.str(), Ranges, ArrayRef()); }