From c4bbd8531e9a8a29751cfe444f6cc621b9440c57 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Sat, 17 Dec 2011 05:26:04 +0000 Subject: [PATCH] Refactor 'TextDiagnostic' to have a parent class 'DiagnosticRenderer' which handles the policy of how diagnostics are lowered/rendered, while TextDiagnostic handles the actual pretty-printing. This is a first part of reworking SerializedDiagnosticPrinter to use the same inclusion-stack/macro-expansion logic as TextDiagnostic. llvm-svn: 146819 --- .../clang/Frontend/DiagnosticRenderer.h | 123 ++++++++ clang/include/clang/Frontend/TextDiagnostic.h | 86 ++--- clang/lib/Frontend/CMakeLists.txt | 1 + clang/lib/Frontend/DiagnosticRenderer.cpp | 297 ++++++++++++++++++ clang/lib/Frontend/TextDiagnostic.cpp | 284 ++--------------- 5 files changed, 471 insertions(+), 320 deletions(-) create mode 100644 clang/include/clang/Frontend/DiagnosticRenderer.h create mode 100644 clang/lib/Frontend/DiagnosticRenderer.cpp diff --git a/clang/include/clang/Frontend/DiagnosticRenderer.h b/clang/include/clang/Frontend/DiagnosticRenderer.h new file mode 100644 index 000000000000..fc9131b4bd74 --- /dev/null +++ b/clang/include/clang/Frontend/DiagnosticRenderer.h @@ -0,0 +1,123 @@ +//===--- DiagnosticRenderer.h - Diagnostic Pretty-Printing ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a utility class that provides support for pretty-printing of +// diagnostics. It is used to implement the different code paths which require +// such functionality in a consistent way. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_DIAGNOSTIC_RENDERER_H_ +#define LLVM_CLANG_FRONTEND_DIAGNOSTIC_RENDERER_H_ + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" + +namespace clang { + class DiagnosticOptions; + class LangOptions; + class SourceManager; + +/// \brief Class to encapsulate the logic for formatting a diagnostic message. +/// Actual "printing" logic is implemented by subclasses. +/// +/// This class provides an interface for building and emitting +/// diagnostic, including all of the macro backtraces, caret diagnostics, FixIt +/// Hints, and code snippets. In the presence of macros this involves +/// a recursive process, synthesizing notes for each macro expansion. +/// +/// A brief worklist: +/// FIXME: Sink the recursive printing of template instantiations into this +/// class. +class DiagnosticRenderer { +protected: + const SourceManager &SM; + const LangOptions &LangOpts; + const DiagnosticOptions &DiagOpts; + + /// \brief The location of the previous diagnostic if known. + /// + /// This will be invalid in cases where there is no (known) previous + /// diagnostic location, or that location itself is invalid or comes from + /// a different source manager than SM. + SourceLocation LastLoc; + + /// \brief The location of the last include whose stack was printed if known. + /// + /// Same restriction as \see LastLoc essentially, but tracking include stack + /// root locations rather than diagnostic locations. + SourceLocation LastIncludeLoc; + + /// \brief The level of the last diagnostic emitted. + /// + /// The level of the last diagnostic emitted. Used to detect level changes + /// which change the amount of information displayed. + DiagnosticsEngine::Level LastLevel; + + DiagnosticRenderer(const SourceManager &SM, + const LangOptions &LangOpts, + const DiagnosticOptions &DiagOpts); + + virtual ~DiagnosticRenderer(); + + virtual void emitDiagnosticMessage(SourceLocation Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef Ranges, + const Diagnostic *Info) = 0; + + virtual void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + ArrayRef Ranges) = 0; + + virtual void emitBasicNote(StringRef Message) = 0; + + virtual void emitCodeContext(SourceLocation Loc, + DiagnosticsEngine::Level Level, + SmallVectorImpl& Ranges, + ArrayRef Hints) = 0; + + virtual void emitIncludeLocation(SourceLocation Loc, PresumedLoc PLoc) = 0; + + virtual void beginDiagnostic(const Diagnostic *Info, + DiagnosticsEngine::Level Level) {} + virtual void endDiagnostic(const Diagnostic *Info, + DiagnosticsEngine::Level Level) {} + + +private: + void emitIncludeStack(SourceLocation Loc, DiagnosticsEngine::Level Level); + void emitIncludeStackRecursively(SourceLocation Loc); + void emitMacroExpansionsAndCarets(SourceLocation Loc, + DiagnosticsEngine::Level Level, + SmallVectorImpl& Ranges, + ArrayRef Hints, + unsigned &MacroDepth, + unsigned OnMacroInst = 0); +public: + /// \brief Emit a diagnostic. + /// + /// This is the primary entry point for emitting diagnostic messages. + /// It handles formatting and rendering the message as well as any ancillary + /// information needed based on macros whose expansions impact the + /// diagnostic. + /// + /// \param Loc The location for this caret. + /// \param Level The level of the diagnostic to be emitted. + /// \param Message The diagnostic message to emit. + /// \param Ranges The underlined ranges for this code snippet. + /// \param FixItHints The FixIt hints active for this diagnostic. + void emitDiagnostic(SourceLocation Loc, DiagnosticsEngine::Level Level, + StringRef Message, ArrayRef Ranges, + ArrayRef FixItHints, + const Diagnostic *Info = 0); +}; +} // end clang namespace +#endif diff --git a/clang/include/clang/Frontend/TextDiagnostic.h b/clang/include/clang/Frontend/TextDiagnostic.h index ce86914bca7b..4c71752c6fa4 100644 --- a/clang/include/clang/Frontend/TextDiagnostic.h +++ b/clang/include/clang/Frontend/TextDiagnostic.h @@ -16,14 +16,9 @@ #ifndef LLVM_CLANG_FRONTEND_TEXT_DIAGNOSTIC_H_ #define LLVM_CLANG_FRONTEND_TEXT_DIAGNOSTIC_H_ -#include "clang/Basic/Diagnostic.h" -#include "clang/Basic/LLVM.h" -#include "clang/Basic/SourceLocation.h" +#include "clang/Frontend/DiagnosticRenderer.h" namespace clang { -class DiagnosticOptions; -class LangOptions; -class SourceManager; /// \brief Class to encapsulate the logic for formatting and printing a textual /// diagnostic message. @@ -37,34 +32,8 @@ class SourceManager; /// beautiful text diagnostics from any particular interfaces. The Clang /// DiagnosticClient is implemented through this class as is diagnostic /// printing coming out of libclang. -/// -/// A brief worklist: -/// FIXME: Sink the recursive printing of template instantiations into this -/// class. -class TextDiagnostic { +class TextDiagnostic : public DiagnosticRenderer { raw_ostream &OS; - const SourceManager &SM; - const LangOptions &LangOpts; - const DiagnosticOptions &DiagOpts; - - /// \brief The location of the previous diagnostic if known. - /// - /// This will be invalid in cases where there is no (known) previous - /// diagnostic location, or that location itself is invalid or comes from - /// a different source manager than SM. - SourceLocation LastLoc; - - /// \brief The location of the last include whose stack was printed if known. - /// - /// Same restriction as \see LastLoc essentially, but tracking include stack - /// root locations rather than diagnostic locations. - SourceLocation LastIncludeLoc; - - /// \brief The level of the last diagnostic emitted. - /// - /// The level of the last diagnostic emitted. Used to detect level changes - /// which change the amount of information displayed. - DiagnosticsEngine::Level LastLevel; public: TextDiagnostic(raw_ostream &OS, @@ -72,22 +41,8 @@ public: const LangOptions &LangOpts, const DiagnosticOptions &DiagOpts); - /// \brief Emit a textual diagnostic. - /// - /// This is the primary entry point for emitting textual diagnostic messages. - /// It handles formatting and printing the message as well as any ancillary - /// information needed based on macros whose expansions impact the - /// diagnostic. - /// - /// \param Loc The location for this caret. - /// \param Level The level of the diagnostic to be emitted. - /// \param Message The diagnostic message to emit. - /// \param Ranges The underlined ranges for this code snippet. - /// \param FixItHints The FixIt hints active for this diagnostic. - void emitDiagnostic(SourceLocation Loc, DiagnosticsEngine::Level Level, - StringRef Message, ArrayRef Ranges, - ArrayRef FixItHints); - + virtual ~TextDiagnostic(); + /// \brief Print the diagonstic level to a raw_ostream. /// /// This is a static helper that handles colorizing the level and formatting @@ -121,18 +76,29 @@ public: unsigned CurrentColumn, unsigned Columns, bool ShowColors); +protected: + virtual void emitDiagnosticMessage(SourceLocation Loc,PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef Ranges, + const Diagnostic *Info); + + virtual void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + ArrayRef Ranges); + + virtual void emitCodeContext(SourceLocation Loc, + DiagnosticsEngine::Level Level, + SmallVectorImpl& Ranges, + ArrayRef Hints) { + emitSnippetAndCaret(Loc, Level, Ranges, Hints); + } + + virtual void emitBasicNote(StringRef Message); + + virtual void emitIncludeLocation(SourceLocation Loc, PresumedLoc PLoc); + private: - void emitIncludeStack(SourceLocation Loc, DiagnosticsEngine::Level Level); - void emitIncludeStackRecursively(SourceLocation Loc); - void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, - DiagnosticsEngine::Level Level, - ArrayRef Ranges); - void emitMacroExpansionsAndCarets(SourceLocation Loc, - DiagnosticsEngine::Level Level, - SmallVectorImpl& Ranges, - ArrayRef Hints, - unsigned &MacroDepth, - unsigned OnMacroInst = 0); void emitSnippetAndCaret(SourceLocation Loc, DiagnosticsEngine::Level Level, SmallVectorImpl& Ranges, ArrayRef Hints); diff --git a/clang/lib/Frontend/CMakeLists.txt b/clang/lib/Frontend/CMakeLists.txt index f76b79566ce8..f98ed11c67c4 100644 --- a/clang/lib/Frontend/CMakeLists.txt +++ b/clang/lib/Frontend/CMakeLists.txt @@ -18,6 +18,7 @@ add_clang_library(clangFrontend CompilerInvocation.cpp CreateInvocationFromCommandLine.cpp DependencyFile.cpp + DiagnosticRenderer.cpp FrontendAction.cpp FrontendActions.cpp FrontendOptions.cpp diff --git a/clang/lib/Frontend/DiagnosticRenderer.cpp b/clang/lib/Frontend/DiagnosticRenderer.cpp new file mode 100644 index 000000000000..293ffb7a4ed9 --- /dev/null +++ b/clang/lib/Frontend/DiagnosticRenderer.cpp @@ -0,0 +1,297 @@ +//===--- 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); +} + +/// \brief Retrieve the name of the immediate macro expansion. +/// +/// This routine starts from a source location, and finds the name of the macro +/// responsible for its immediate expansion. It looks through any intervening +/// macro argument expansions to compute this. It returns a StringRef which +/// refers to the SourceManager-owned buffer of the source where that macro +/// name is spelled. Thus, the result shouldn't out-live that SourceManager. +/// +static StringRef getImmediateMacroName(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts) { + assert(Loc.isMacroID() && "Only reasonble to call this on macros"); + // Walk past macro argument expanions. + while (SM.isMacroArgExpansion(Loc)) + Loc = SM.getImmediateExpansionRange(Loc).first; + + // Find the spelling location of the start of the non-argument expansion + // range. This is where the macro name was spelled in order to begin + // expanding this macro. + Loc = SM.getSpellingLoc(SM.getImmediateExpansionRange(Loc).first); + + // Dig out the buffer where the macro name was spelled and the extents of the + // name so that we can render it into the expansion note. + std::pair ExpansionInfo = SM.getDecomposedLoc(Loc); + unsigned MacroTokenLength = Lexer::MeasureTokenLength(Loc, SM, LangOpts); + StringRef ExpansionBuffer = SM.getBufferData(ExpansionInfo.first); + return ExpansionBuffer.substr(ExpansionInfo.second, MacroTokenLength); +} + +/// 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 '" + << getImmediateMacroName(MacroLoc, SM, LangOpts) << "'"; + emitDiagnostic(SM.getSpellingLoc(Loc), DiagnosticsEngine::Note, + Message.str(), + Ranges, ArrayRef()); +} + diff --git a/clang/lib/Frontend/TextDiagnostic.cpp b/clang/lib/Frontend/TextDiagnostic.cpp index 425bc26fccfc..d2b8660c625e 100644 --- a/clang/lib/Frontend/TextDiagnostic.cpp +++ b/clang/lib/Frontend/TextDiagnostic.cpp @@ -174,70 +174,6 @@ static void selectInterestingSourceRegion(std::string &SourceLine, } } -/// 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); -} - /// \brief Skip over whitespace in the string, starting at the given /// index. /// @@ -389,85 +325,33 @@ static bool printWordWrapped(raw_ostream &OS, StringRef Str, return Wrapped; } -/// \brief Retrieve the name of the immediate macro expansion. -/// -/// This routine starts from a source location, and finds the name of the macro -/// responsible for its immediate expansion. It looks through any intervening -/// macro argument expansions to compute this. It returns a StringRef which -/// refers to the SourceManager-owned buffer of the source where that macro -/// name is spelled. Thus, the result shouldn't out-live that SourceManager. -/// -static StringRef getImmediateMacroName(SourceLocation Loc, - const SourceManager &SM, - const LangOptions &LangOpts) { - assert(Loc.isMacroID() && "Only reasonble to call this on macros"); - // Walk past macro argument expanions. - while (SM.isMacroArgExpansion(Loc)) - Loc = SM.getImmediateExpansionRange(Loc).first; - - // Find the spelling location of the start of the non-argument expansion - // range. This is where the macro name was spelled in order to begin - // expanding this macro. - Loc = SM.getSpellingLoc(SM.getImmediateExpansionRange(Loc).first); - - // Dig out the buffer where the macro name was spelled and the extents of the - // name so that we can render it into the expansion note. - std::pair ExpansionInfo = SM.getDecomposedLoc(Loc); - unsigned MacroTokenLength = Lexer::MeasureTokenLength(Loc, SM, LangOpts); - StringRef ExpansionBuffer = SM.getBufferData(ExpansionInfo.first); - return ExpansionBuffer.substr(ExpansionInfo.second, MacroTokenLength); -} - TextDiagnostic::TextDiagnostic(raw_ostream &OS, const SourceManager &SM, const LangOptions &LangOpts, const DiagnosticOptions &DiagOpts) - : OS(OS), SM(SM), LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() { -} + : DiagnosticRenderer(SM, LangOpts, DiagOpts), OS(OS) {} -void TextDiagnostic::emitDiagnostic(SourceLocation Loc, - DiagnosticsEngine::Level Level, - StringRef Message, - ArrayRef Ranges, - ArrayRef FixItHints) { - 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); +TextDiagnostic::~TextDiagnostic() {} +void +TextDiagnostic::emitDiagnosticMessage(SourceLocation Loc, + PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef Ranges, + const Diagnostic *Info) { uint64_t StartOfLocationInfo = OS.tell(); - // Next emit the location of this particular diagnostic. + // Emit the location of this particular diagnostic. emitDiagnosticLoc(Loc, PLoc, Level, Ranges); - + if (DiagOpts.ShowColors) OS.resetColor(); - + printDiagnosticLevel(OS, Level, DiagOpts.ShowColors); printDiagnosticMessage(OS, Level, Message, OS.tell() - StartOfLocationInfo, DiagOpts.MessageLength, DiagOpts.ShowColors); - - // 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; } /*static*/ void @@ -525,50 +409,6 @@ TextDiagnostic::printDiagnosticMessage(raw_ostream &OS, OS << '\n'; } -/// \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 TextDiagnostic::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 TextDiagnostic::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()); - - if (DiagOpts.ShowLocation) - OS << "In file included from " << PLoc.getFilename() - << ':' << PLoc.getLine() << ":\n"; - else - OS << "In included file:\n"; -} - /// \brief Print out the file/line/column information and include trace. /// /// This method handlen the emission of the diagnostic location information. @@ -676,95 +516,19 @@ void TextDiagnostic::emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, OS << ' '; } -/// \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 TextDiagnostic::emitMacroExpansionsAndCarets( - SourceLocation Loc, - DiagnosticsEngine::Level Level, - SmallVectorImpl& Ranges, - ArrayRef Hints, - unsigned &MacroDepth, - unsigned OnMacroInst) { - assert(!Loc.isInvalid() && "must have a valid source location here"); +void TextDiagnostic::emitBasicNote(StringRef Message) { + // FIXME: Emit this as a real note diagnostic. + // FIXME: Format an actual diagnostic rather than a hard coded string. + OS << "note: " << Message << "\n"; +} - // 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; - emitSnippetAndCaret(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) { - // FIXME: Emit this as a real note diagnostic. - // FIXME: Format an actual diagnostic rather than a hard coded string. - OS << "note: (skipping " << (MacroSkipEnd - MacroSkipStart) - << " expansions in backtrace; use -fmacro-backtrace-limit=0 to see " - "all)\n"; - } - return; - } - - llvm::SmallString<100> MessageStorage; - llvm::raw_svector_ostream Message(MessageStorage); - Message << "expanded from macro '" - << getImmediateMacroName(MacroLoc, SM, LangOpts) << "'"; - emitDiagnostic(SM.getSpellingLoc(Loc), DiagnosticsEngine::Note, - Message.str(), - Ranges, ArrayRef()); +void TextDiagnostic::emitIncludeLocation(SourceLocation Loc, + PresumedLoc PLoc) { + if (DiagOpts.ShowLocation) + OS << "In file included from " << PLoc.getFilename() << ':' + << PLoc.getLine() << ":\n"; + else + OS << "In included file:\n"; } /// \brief Emit a code snippet and caret line.