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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@146819 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Ted Kremenek 2011-12-17 05:26:04 +00:00
Родитель 373cb78320
Коммит 2898d4f764
5 изменённых файлов: 471 добавлений и 320 удалений

Просмотреть файл

@ -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<CharSourceRange> Ranges,
const Diagnostic *Info) = 0;
virtual void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
DiagnosticsEngine::Level Level,
ArrayRef<CharSourceRange> Ranges) = 0;
virtual void emitBasicNote(StringRef Message) = 0;
virtual void emitCodeContext(SourceLocation Loc,
DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange>& Ranges,
ArrayRef<FixItHint> 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<CharSourceRange>& Ranges,
ArrayRef<FixItHint> 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<CharSourceRange> Ranges,
ArrayRef<FixItHint> FixItHints,
const Diagnostic *Info = 0);
};
} // end clang namespace
#endif

Просмотреть файл

@ -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<CharSourceRange> Ranges,
ArrayRef<FixItHint> 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<CharSourceRange> Ranges,
const Diagnostic *Info);
virtual void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
DiagnosticsEngine::Level Level,
ArrayRef<CharSourceRange> Ranges);
virtual void emitCodeContext(SourceLocation Loc,
DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange>& Ranges,
ArrayRef<FixItHint> 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<CharSourceRange> Ranges);
void emitMacroExpansionsAndCarets(SourceLocation Loc,
DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange>& Ranges,
ArrayRef<FixItHint> Hints,
unsigned &MacroDepth,
unsigned OnMacroInst = 0);
void emitSnippetAndCaret(SourceLocation Loc, DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange>& Ranges,
ArrayRef<FixItHint> Hints);

Просмотреть файл

@ -18,6 +18,7 @@ add_clang_library(clangFrontend
CompilerInvocation.cpp
CreateInvocationFromCommandLine.cpp
DependencyFile.cpp
DiagnosticRenderer.cpp
FrontendAction.cpp
FrontendActions.cpp
FrontendOptions.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 <algorithm>
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<FileID, unsigned> 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<CharSourceRange> Ranges,
ArrayRef<FixItHint> 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<CharSourceRange, 20> MutableRanges(Ranges.begin(),
Ranges.end());
for (ArrayRef<FixItHint>::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<CharSourceRange>& Ranges,
ArrayRef<FixItHint> 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<CharSourceRange>::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<FixItHint>());
}

Просмотреть файл

@ -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<FileID, unsigned> 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<CharSourceRange> Ranges,
ArrayRef<FixItHint> 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<clang::CharSourceRange> 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<CharSourceRange, 20> MutableRanges(Ranges.begin(),
Ranges.end());
for (ArrayRef<FixItHint>::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<CharSourceRange>& Ranges,
ArrayRef<FixItHint> 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<CharSourceRange>::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<FixItHint>());
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.