123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514 |
- //===--- 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/DiagnosticOptions.h"
- #include "clang/Basic/FileManager.h"
- #include "clang/Basic/SourceManager.h"
- #include "clang/Edit/Commit.h"
- #include "clang/Edit/EditedSource.h"
- #include "clang/Edit/EditsReceiver.h"
- #include "clang/Lex/Lexer.h"
- #include "llvm/ADT/SmallSet.h"
- #include "llvm/ADT/SmallString.h"
- #include "llvm/Support/ErrorHandling.h"
- #include "llvm/Support/MemoryBuffer.h"
- #include "llvm/Support/raw_ostream.h"
- #include <algorithm>
- using namespace clang;
- /// \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.
- ///
- /// This differs from Lexer::getImmediateMacroName in that any macro argument
- /// location will result in the topmost function macro that accepted it.
- /// e.g.
- /// \code
- /// MAC1( MAC2(foo) )
- /// \endcode
- /// for location of 'foo' token, this function will return "MAC1" while
- /// Lexer::getImmediateMacroName will return "MAC2".
- 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;
- // If the macro's spelling has no FileID, then it's actually a token paste
- // or stringization (or similar) and not a macro at all.
- if (!SM.getFileEntryForID(SM.getFileID(SM.getSpellingLoc(Loc))))
- return StringRef();
- // 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);
- }
- DiagnosticRenderer::DiagnosticRenderer(const LangOptions &LangOpts,
- 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) { }
- void insert(SourceLocation loc, StringRef text) override {
- MergedFixits.push_back(FixItHint::CreateInsertion(loc, text));
- }
- void replace(CharSourceRange range, StringRef text) override {
- 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, /*afterToken=*/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,
- /*afterToken=*/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);
- if (!Loc.isValid())
- // If we have no source location, just emit the diagnostic message.
- emitDiagnosticMessage(Loc, PresumedLoc(), Level, Message, Ranges, SM, D);
- else {
- // Get the ranges into a local array we can hack on.
- SmallVector<CharSourceRange, 20> MutableRanges(Ranges.begin(),
- Ranges.end());
- 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);
- SourceLocation UnexpandedLoc = Loc;
- // Find the ultimate expansion location for the diagnostic.
- Loc = SM->getFileLoc(Loc);
- PresumedLoc PLoc = SM->getPresumedLoc(Loc, DiagOpts->ShowPresumedLoc);
- // First, if this diagnostic is not in the main file, print out the
- // "included from" lines.
- emitIncludeStack(Loc, PLoc, Level, *SM);
- // Next, emit the actual diagnostic message and caret.
- emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, SM, D);
- emitCaret(Loc, Level, MutableRanges, FixItHints, *SM);
- // If this location is within a macro, walk from UnexpandedLoc up to Loc
- // and produce a macro backtrace.
- if (UnexpandedLoc.isValid() && UnexpandedLoc.isMacroID()) {
- unsigned MacroDepth = 0;
- emitMacroExpansions(UnexpandedLoc, 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()
- : nullptr,
- &Diag);
- }
- void DiagnosticRenderer::emitBasicNote(StringRef Message) {
- emitDiagnosticMessage(
- SourceLocation(), PresumedLoc(), DiagnosticsEngine::Note, Message,
- ArrayRef<CharSourceRange>(), nullptr, DiagOrStoredDiag());
- }
- /// \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 Loc The diagnostic location.
- /// \param PLoc The presumed location of the diagnostic location.
- /// \param Level The diagnostic level of the message this stack pertains to.
- void DiagnosticRenderer::emitIncludeStack(SourceLocation Loc,
- PresumedLoc PLoc,
- DiagnosticsEngine::Level Level,
- const SourceManager &SM) {
- SourceLocation IncludeLoc = PLoc.getIncludeLoc();
- // Skip redundant include stacks altogether.
- if (LastIncludeLoc == IncludeLoc)
- return;
-
- LastIncludeLoc = IncludeLoc;
-
- if (!DiagOpts->ShowNoteIncludeStack && Level == DiagnosticsEngine::Note)
- return;
- if (IncludeLoc.isValid())
- emitIncludeStackRecursively(IncludeLoc, SM);
- else {
- emitModuleBuildStack(SM);
- emitImportStack(Loc, SM);
- }
- }
- /// \brief Helper to recursivly walk up the include stack and print each layer
- /// on the way back down.
- void DiagnosticRenderer::emitIncludeStackRecursively(SourceLocation Loc,
- const SourceManager &SM) {
- if (Loc.isInvalid()) {
- emitModuleBuildStack(SM);
- return;
- }
-
- PresumedLoc PLoc = SM.getPresumedLoc(Loc, DiagOpts->ShowPresumedLoc);
- if (PLoc.isInvalid())
- return;
- // If this source location was imported from a module, print the module
- // import stack rather than the
- // FIXME: We want submodule granularity here.
- std::pair<SourceLocation, StringRef> Imported = SM.getModuleImportLoc(Loc);
- if (Imported.first.isValid()) {
- // This location was imported by a module. Emit the module import stack.
- emitImportStackRecursively(Imported.first, Imported.second, SM);
- return;
- }
- // Emit the other include frames first.
- emitIncludeStackRecursively(PLoc.getIncludeLoc(), SM);
-
- // Emit the inclusion text/note.
- emitIncludeLocation(Loc, PLoc, SM);
- }
- /// \brief Emit the module import stack associated with the current location.
- void DiagnosticRenderer::emitImportStack(SourceLocation Loc,
- const SourceManager &SM) {
- if (Loc.isInvalid()) {
- emitModuleBuildStack(SM);
- return;
- }
- std::pair<SourceLocation, StringRef> NextImportLoc
- = SM.getModuleImportLoc(Loc);
- emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second, SM);
- }
- /// \brief Helper to recursivly walk up the import stack and print each layer
- /// on the way back down.
- void DiagnosticRenderer::emitImportStackRecursively(SourceLocation Loc,
- StringRef ModuleName,
- const SourceManager &SM) {
- if (Loc.isInvalid()) {
- return;
- }
- PresumedLoc PLoc = SM.getPresumedLoc(Loc, DiagOpts->ShowPresumedLoc);
- if (PLoc.isInvalid())
- return;
- // Emit the other import frames first.
- std::pair<SourceLocation, StringRef> NextImportLoc
- = SM.getModuleImportLoc(Loc);
- emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second, SM);
- // Emit the inclusion text/note.
- emitImportLocation(Loc, PLoc, ModuleName, SM);
- }
- /// \brief Emit the module build stack, for cases where a module is (re-)built
- /// on demand.
- void DiagnosticRenderer::emitModuleBuildStack(const SourceManager &SM) {
- ModuleBuildStack Stack = SM.getModuleBuildStack();
- for (unsigned I = 0, N = Stack.size(); I != N; ++I) {
- const SourceManager &CurSM = Stack[I].second.getManager();
- SourceLocation CurLoc = Stack[I].second;
- emitBuildingModuleLocation(CurLoc,
- CurSM.getPresumedLoc(CurLoc,
- DiagOpts->ShowPresumedLoc),
- Stack[I].first,
- CurSM);
- }
- }
- // Helper function to fix up source ranges. It takes in an array of ranges,
- // and outputs an array of ranges where we want to draw the range highlighting
- // around the location specified by CaretLoc.
- //
- // To find locations which correspond to the caret, we crawl the macro caller
- // chain for the beginning and end of each range. If the caret location
- // is in a macro expansion, we search each chain for a location
- // in the same expansion as the caret; otherwise, we crawl to the top of
- // each chain. Two locations are part of the same macro expansion
- // iff the FileID is the same.
- static void mapDiagnosticRanges(
- SourceLocation CaretLoc,
- ArrayRef<CharSourceRange> Ranges,
- SmallVectorImpl<CharSourceRange> &SpellingRanges,
- const SourceManager *SM) {
- FileID CaretLocFileID = SM->getFileID(CaretLoc);
- for (ArrayRef<CharSourceRange>::const_iterator I = Ranges.begin(),
- E = Ranges.end();
- I != E; ++I) {
- SourceLocation Begin = I->getBegin(), End = I->getEnd();
- bool IsTokenRange = I->isTokenRange();
- FileID BeginFileID = SM->getFileID(Begin);
- FileID EndFileID = SM->getFileID(End);
- // Find the common parent for the beginning and end of the range.
- // First, crawl the expansion chain for the beginning of the range.
- llvm::SmallDenseMap<FileID, SourceLocation> BeginLocsMap;
- while (Begin.isMacroID() && BeginFileID != EndFileID) {
- BeginLocsMap[BeginFileID] = Begin;
- Begin = SM->getImmediateExpansionRange(Begin).first;
- BeginFileID = SM->getFileID(Begin);
- }
- // Then, crawl the expansion chain for the end of the range.
- if (BeginFileID != EndFileID) {
- while (End.isMacroID() && !BeginLocsMap.count(EndFileID)) {
- End = SM->getImmediateExpansionRange(End).second;
- EndFileID = SM->getFileID(End);
- }
- if (End.isMacroID()) {
- Begin = BeginLocsMap[EndFileID];
- BeginFileID = EndFileID;
- }
- }
- while (Begin.isMacroID() && BeginFileID != CaretLocFileID) {
- if (SM->isMacroArgExpansion(Begin)) {
- Begin = SM->getImmediateSpellingLoc(Begin);
- End = SM->getImmediateSpellingLoc(End);
- } else {
- Begin = SM->getImmediateExpansionRange(Begin).first;
- End = SM->getImmediateExpansionRange(End).second;
- }
- BeginFileID = SM->getFileID(Begin);
- if (BeginFileID != SM->getFileID(End)) {
- // FIXME: Ugly hack to stop a crash; this code is making bad
- // assumptions and it's too complicated for me to reason
- // about.
- Begin = End = SourceLocation();
- break;
- }
- }
- // Return the spelling location of the beginning and end of the range.
- Begin = SM->getSpellingLoc(Begin);
- End = SM->getSpellingLoc(End);
- SpellingRanges.push_back(CharSourceRange(SourceRange(Begin, End),
- IsTokenRange));
- }
- }
- void DiagnosticRenderer::emitCaret(SourceLocation Loc,
- DiagnosticsEngine::Level Level,
- ArrayRef<CharSourceRange> Ranges,
- ArrayRef<FixItHint> Hints,
- const SourceManager &SM) {
- SmallVector<CharSourceRange, 4> SpellingRanges;
- mapDiagnosticRanges(Loc, Ranges, SpellingRanges, &SM);
- emitCodeContext(Loc, Level, SpellingRanges, Hints, SM);
- }
- /// \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 OnMacroInst The current depth of the macro expansion stack.
- void DiagnosticRenderer::emitMacroExpansions(SourceLocation Loc,
- DiagnosticsEngine::Level Level,
- ArrayRef<CharSourceRange> Ranges,
- ArrayRef<FixItHint> Hints,
- const SourceManager &SM,
- unsigned &MacroDepth,
- unsigned OnMacroInst) {
- assert(!Loc.isInvalid() && "must have a valid source location here");
- // Walk up to the caller of this macro, and produce a backtrace down to there.
- SourceLocation OneLevelUp = SM.getImmediateMacroCallerLoc(Loc);
- if (OneLevelUp.isMacroID())
- emitMacroExpansions(OneLevelUp, Level, Ranges, Hints, SM,
- MacroDepth, OnMacroInst + 1);
- else
- MacroDepth = OnMacroInst + 1;
- unsigned MacroSkipStart = 0, MacroSkipEnd = 0;
- if (MacroDepth > DiagOpts->MacroBacktraceLimit &&
- DiagOpts->MacroBacktraceLimit != 0) {
- 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);
- if (Suppressed) {
- // Tell the user that we've skipped contexts.
- 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;
- }
- // Find the spelling location for the macro definition. We must use the
- // spelling location here to avoid emitting a macro bactrace for the note.
- SourceLocation SpellingLoc = Loc;
- // If this is the expansion of a macro argument, point the caret at the
- // use of the argument in the definition of the macro, not the expansion.
- if (SM.isMacroArgExpansion(Loc))
- SpellingLoc = SM.getImmediateExpansionRange(Loc).first;
- SpellingLoc = SM.getSpellingLoc(SpellingLoc);
- // Map the ranges into the FileID of the diagnostic location.
- SmallVector<CharSourceRange, 4> SpellingRanges;
- mapDiagnosticRanges(Loc, Ranges, SpellingRanges, &SM);
- SmallString<100> MessageStorage;
- llvm::raw_svector_ostream Message(MessageStorage);
- StringRef MacroName = getImmediateMacroName(Loc, SM, LangOpts);
- if (MacroName.empty())
- Message << "expanded from here";
- else
- Message << "expanded from macro '" << MacroName << "'";
- emitDiagnostic(SpellingLoc, DiagnosticsEngine::Note, Message.str(),
- SpellingRanges, None, &SM);
- }
- DiagnosticNoteRenderer::~DiagnosticNoteRenderer() {}
- void DiagnosticNoteRenderer::emitIncludeLocation(SourceLocation Loc,
- PresumedLoc PLoc,
- const SourceManager &SM) {
- // Generate a note indicating the include location.
- 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::emitImportLocation(SourceLocation Loc,
- PresumedLoc PLoc,
- StringRef ModuleName,
- const SourceManager &SM) {
- // Generate a note indicating the include location.
- SmallString<200> MessageStorage;
- llvm::raw_svector_ostream Message(MessageStorage);
- Message << "in module '" << ModuleName << "' imported from "
- << PLoc.getFilename() << ':' << PLoc.getLine() << ":";
- emitNote(Loc, Message.str(), &SM);
- }
- void
- DiagnosticNoteRenderer::emitBuildingModuleLocation(SourceLocation Loc,
- PresumedLoc PLoc,
- StringRef ModuleName,
- const SourceManager &SM) {
- // Generate a note indicating the include location.
- SmallString<200> MessageStorage;
- llvm::raw_svector_ostream Message(MessageStorage);
- if (PLoc.getFilename())
- Message << "while building module '" << ModuleName << "' imported from "
- << PLoc.getFilename() << ':' << PLoc.getLine() << ":";
- else
- Message << "while building module '" << ModuleName << "':";
- emitNote(Loc, Message.str(), &SM);
- }
|