DiagnosticRenderer.cpp [plain text]
#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 "clang/Edit/EditedSource.h"
#include "clang/Edit/Commit.h"
#include "clang/Edit/EditsReceiver.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallString.h"
#include <algorithm>
using namespace clang;
static StringRef getImmediateMacroName(SourceLocation Loc,
const SourceManager &SM,
const LangOptions &LangOpts) {
assert(Loc.isMacroID() && "Only reasonble to call this on macros");
while (SM.isMacroArgExpansion(Loc))
Loc = SM.getImmediateExpansionRange(Loc).first;
Loc = SM.getSpellingLoc(SM.getImmediateExpansionRange(Loc).first);
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);
}
DiagnosticRenderer::DiagnosticRenderer(const LangOptions &LangOpts,
const DiagnosticOptions &DiagOpts)
: LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {}
DiagnosticRenderer::~DiagnosticRenderer() {}
namespace {
class FixitReceiver : public edit::EditsReceiver {
SmallVectorImpl<FixItHint> &MergedFixits;
public:
FixitReceiver(SmallVectorImpl<FixItHint> &MergedFixits)
: MergedFixits(MergedFixits) { }
virtual void insert(SourceLocation loc, StringRef text) {
MergedFixits.push_back(FixItHint::CreateInsertion(loc, text));
}
virtual void replace(CharSourceRange range, StringRef text) {
MergedFixits.push_back(FixItHint::CreateReplacement(range, text));
}
};
}
static void mergeFixits(ArrayRef<FixItHint> FixItHints,
const SourceManager &SM, const LangOptions &LangOpts,
SmallVectorImpl<FixItHint> &MergedFixits) {
edit::Commit commit(SM, LangOpts);
for (ArrayRef<FixItHint>::const_iterator
I = FixItHints.begin(), E = FixItHints.end(); I != E; ++I) {
const FixItHint &Hint = *I;
if (Hint.CodeToInsert.empty()) {
if (Hint.InsertFromRange.isValid())
commit.insertFromRange(Hint.RemoveRange.getBegin(),
Hint.InsertFromRange, false,
Hint.BeforePreviousInsertions);
else
commit.remove(Hint.RemoveRange);
} else {
if (Hint.RemoveRange.isTokenRange() ||
Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd())
commit.replace(Hint.RemoveRange, Hint.CodeToInsert);
else
commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert,
false, Hint.BeforePreviousInsertions);
}
}
edit::EditedSource Editor(SM, LangOpts);
if (Editor.commit(commit)) {
FixitReceiver Rec(MergedFixits);
Editor.applyRewrites(Rec);
}
}
void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc,
DiagnosticsEngine::Level Level,
StringRef Message,
ArrayRef<CharSourceRange> Ranges,
ArrayRef<FixItHint> FixItHints,
const SourceManager *SM,
DiagOrStoredDiag D) {
assert(SM || Loc.isInvalid());
beginDiagnostic(D, Level);
PresumedLoc PLoc;
if (Loc.isValid()) {
PLoc = SM->getPresumedLocForDisplay(Loc);
emitIncludeStack(PLoc.getIncludeLoc(), Level, *SM);
}
emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, SM, D);
if (Loc.isValid()) {
SmallVector<CharSourceRange, 20> MutableRanges(Ranges.begin(),
Ranges.end());
llvm::SmallVector<FixItHint, 8> MergedFixits;
if (!FixItHints.empty()) {
mergeFixits(FixItHints, *SM, LangOpts, MergedFixits);
FixItHints = MergedFixits;
}
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, *SM,
MacroDepth);
}
LastLoc = Loc;
LastLevel = Level;
endDiagnostic(D, Level);
}
void DiagnosticRenderer::emitStoredDiagnostic(StoredDiagnostic &Diag) {
emitDiagnostic(Diag.getLocation(), Diag.getLevel(), Diag.getMessage(),
Diag.getRanges(), Diag.getFixIts(),
Diag.getLocation().isValid() ? &Diag.getLocation().getManager()
: 0,
&Diag);
}
void DiagnosticRenderer::emitIncludeStack(SourceLocation Loc,
DiagnosticsEngine::Level Level,
const SourceManager &SM) {
if (LastIncludeLoc == Loc)
return;
LastIncludeLoc = Loc;
if (!DiagOpts.ShowNoteIncludeStack && Level == DiagnosticsEngine::Note)
return;
emitIncludeStackRecursively(Loc, SM);
}
void DiagnosticRenderer::emitIncludeStackRecursively(SourceLocation Loc,
const SourceManager &SM) {
if (Loc.isInvalid())
return;
PresumedLoc PLoc = SM.getPresumedLoc(Loc);
if (PLoc.isInvalid())
return;
emitIncludeStackRecursively(PLoc.getIncludeLoc(), SM);
emitIncludeLocation(Loc, PLoc, SM);
}
static void mapDiagnosticRanges(
SourceLocation CaretLoc,
const SmallVectorImpl<CharSourceRange>& Ranges,
SmallVectorImpl<CharSourceRange>& SpellingRanges,
const SourceManager *SM) {
FileID CaretLocFileID = SM->getFileID(CaretLoc);
for (SmallVectorImpl<CharSourceRange>::const_iterator I = Ranges.begin(),
E = Ranges.end();
I != E; ++I) {
SourceLocation Begin = I->getBegin(), End = I->getEnd();
bool IsTokenRange = I->isTokenRange();
while (Begin.isMacroID() && SM->getFileID(Begin) != CaretLocFileID)
Begin = SM->getImmediateMacroCallerLoc(Begin);
while (End.isMacroID() && SM->getFileID(End) != CaretLocFileID) {
if (SM->isMacroArgExpansion(End)) {
End = SM->getImmediateSpellingLoc(End);
} else {
End = SM->getImmediateExpansionRange(End).second;
}
}
Begin = SM->getSpellingLoc(Begin);
End = SM->getSpellingLoc(End);
SpellingRanges.push_back(CharSourceRange(SourceRange(Begin, End),
IsTokenRange));
}
}
void DiagnosticRenderer::emitMacroExpansionsAndCarets(
SourceLocation Loc,
DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange>& Ranges,
ArrayRef<FixItHint> Hints,
const SourceManager &SM,
unsigned &MacroDepth,
unsigned OnMacroInst)
{
assert(!Loc.isInvalid() && "must have a valid source location here");
if (Loc.isFileID()) {
SmallVector<CharSourceRange, 4> SpellingRanges;
mapDiagnosticRanges(Loc, Ranges, SpellingRanges, &SM);
assert(MacroDepth == 0 && "We shouldn't hit a leaf node twice!");
MacroDepth = OnMacroInst;
emitCodeContext(Loc, Level, SpellingRanges, Hints, SM);
return;
}
Loc = SM.skipToMacroArgExpansion(Loc);
SourceLocation OneLevelUp = SM.getImmediateMacroCallerLoc(Loc);
emitMacroExpansionsAndCarets(OneLevelUp, Level, Ranges, Hints, SM, MacroDepth,
OnMacroInst + 1);
SourceLocation MacroLoc = Loc;
Loc = SM.getImmediateMacroCalleeLoc(Loc);
unsigned MacroSkipStart = 0, MacroSkipEnd = 0;
if (MacroDepth > DiagOpts.MacroBacktraceLimit &&
DiagOpts.MacroBacktraceLimit != 0) {
MacroSkipStart = DiagOpts.MacroBacktraceLimit / 2 +
DiagOpts.MacroBacktraceLimit % 2;
MacroSkipEnd = MacroDepth - DiagOpts.MacroBacktraceLimit / 2;
}
bool Suppressed = (OnMacroInst >= MacroSkipStart &&
OnMacroInst < MacroSkipEnd);
if (Suppressed) {
if (OnMacroInst == MacroSkipStart) {
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;
}
SmallVector<CharSourceRange, 4> SpellingRanges;
mapDiagnosticRanges(MacroLoc, Ranges, SpellingRanges, &SM);
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(),
SpellingRanges, ArrayRef<FixItHint>(), &SM);
}
DiagnosticNoteRenderer::~DiagnosticNoteRenderer() {}
void DiagnosticNoteRenderer::emitIncludeLocation(SourceLocation Loc,
PresumedLoc PLoc,
const SourceManager &SM) {
SmallString<200> MessageStorage;
llvm::raw_svector_ostream Message(MessageStorage);
Message << "in file included from " << PLoc.getFilename() << ':'
<< PLoc.getLine() << ":";
emitNote(Loc, Message.str(), &SM);
}
void DiagnosticNoteRenderer::emitBasicNote(StringRef Message) {
emitNote(SourceLocation(), Message, 0);
}