зеркало из https://github.com/microsoft/clang-1.git
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:
Родитель
373cb78320
Коммит
2898d4f764
|
@ -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.
|
||||
|
|
Загрузка…
Ссылка в новой задаче