diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp | 317 |
1 files changed, 235 insertions, 82 deletions
diff --git a/contrib/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp b/contrib/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp index c63e98d..caf1f0d 100644 --- a/contrib/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp +++ b/contrib/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp @@ -169,9 +169,7 @@ void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc, // 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); + emitMacroExpansions(UnexpandedLoc, Level, MutableRanges, FixItHints, *SM); } } @@ -247,7 +245,7 @@ void DiagnosticRenderer::emitIncludeStackRecursively(SourceLocation Loc, // import stack rather than the // FIXME: We want submodule granularity here. std::pair<SourceLocation, StringRef> Imported = SM.getModuleImportLoc(Loc); - if (Imported.first.isValid()) { + if (!Imported.second.empty()) { // This location was imported by a module. Emit the module import stack. emitImportStackRecursively(Imported.first, Imported.second, SM); return; @@ -278,13 +276,11 @@ void DiagnosticRenderer::emitImportStack(SourceLocation Loc, void DiagnosticRenderer::emitImportStackRecursively(SourceLocation Loc, StringRef ModuleName, const SourceManager &SM) { - if (Loc.isInvalid()) { + if (ModuleName.empty()) { return; } PresumedLoc PLoc = SM.getPresumedLoc(Loc, DiagOpts->ShowPresumedLoc); - if (PLoc.isInvalid()) - return; // Emit the other import frames first. std::pair<SourceLocation, StringRef> NextImportLoc @@ -310,6 +306,81 @@ void DiagnosticRenderer::emitModuleBuildStack(const SourceManager &SM) { } } +/// A recursive function to trace all possible backtrace locations +/// to match the \p CaretLocFileID. +static SourceLocation +retrieveMacroLocation(SourceLocation Loc, FileID MacroFileID, + FileID CaretFileID, + const SmallVectorImpl<FileID> &CommonArgExpansions, + bool IsBegin, const SourceManager *SM) { + assert(SM->getFileID(Loc) == MacroFileID); + if (MacroFileID == CaretFileID) + return Loc; + if (!Loc.isMacroID()) + return SourceLocation(); + + SourceLocation MacroLocation, MacroArgLocation; + + if (SM->isMacroArgExpansion(Loc)) { + // Only look at the immediate spelling location of this macro argument if + // the other location in the source range is also present in that expansion. + if (std::binary_search(CommonArgExpansions.begin(), + CommonArgExpansions.end(), MacroFileID)) + MacroLocation = SM->getImmediateSpellingLoc(Loc); + MacroArgLocation = IsBegin ? SM->getImmediateExpansionRange(Loc).first + : SM->getImmediateExpansionRange(Loc).second; + } else { + MacroLocation = IsBegin ? SM->getImmediateExpansionRange(Loc).first + : SM->getImmediateExpansionRange(Loc).second; + MacroArgLocation = SM->getImmediateSpellingLoc(Loc); + } + + if (MacroLocation.isValid()) { + MacroFileID = SM->getFileID(MacroLocation); + MacroLocation = + retrieveMacroLocation(MacroLocation, MacroFileID, CaretFileID, + CommonArgExpansions, IsBegin, SM); + if (MacroLocation.isValid()) + return MacroLocation; + } + + MacroFileID = SM->getFileID(MacroArgLocation); + return retrieveMacroLocation(MacroArgLocation, MacroFileID, CaretFileID, + CommonArgExpansions, IsBegin, SM); +} + +/// Walk up the chain of macro expansions and collect the FileIDs identifying the +/// expansions. +static void getMacroArgExpansionFileIDs(SourceLocation Loc, + SmallVectorImpl<FileID> &IDs, + bool IsBegin, const SourceManager *SM) { + while (Loc.isMacroID()) { + if (SM->isMacroArgExpansion(Loc)) { + IDs.push_back(SM->getFileID(Loc)); + Loc = SM->getImmediateSpellingLoc(Loc); + } else { + auto ExpRange = SM->getImmediateExpansionRange(Loc); + Loc = IsBegin ? ExpRange.first : ExpRange.second; + } + } +} + +/// Collect the expansions of the begin and end locations and compute the set +/// intersection. Produces a sorted vector of FileIDs in CommonArgExpansions. +static void computeCommonMacroArgExpansionFileIDs( + SourceLocation Begin, SourceLocation End, const SourceManager *SM, + SmallVectorImpl<FileID> &CommonArgExpansions) { + SmallVector<FileID, 4> BeginArgExpansions; + SmallVector<FileID, 4> EndArgExpansions; + getMacroArgExpansionFileIDs(Begin, BeginArgExpansions, /*IsBegin=*/true, SM); + getMacroArgExpansionFileIDs(End, EndArgExpansions, /*IsBegin=*/false, SM); + std::sort(BeginArgExpansions.begin(), BeginArgExpansions.end()); + std::sort(EndArgExpansions.begin(), EndArgExpansions.end()); + std::set_intersection(BeginArgExpansions.begin(), BeginArgExpansions.end(), + EndArgExpansions.begin(), EndArgExpansions.end(), + std::back_inserter(CommonArgExpansions)); +} + // 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. @@ -327,9 +398,9 @@ static void mapDiagnosticRanges( const SourceManager *SM) { FileID CaretLocFileID = SM->getFileID(CaretLoc); - for (ArrayRef<CharSourceRange>::const_iterator I = Ranges.begin(), - E = Ranges.end(); - I != E; ++I) { + for (auto I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { + if (I->isInvalid()) continue; + SourceLocation Begin = I->getBegin(), End = I->getEnd(); bool IsTokenRange = I->isTokenRange(); @@ -358,27 +429,19 @@ static void mapDiagnosticRanges( } } - 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; - } - } + // Do the backtracking. + SmallVector<FileID, 4> CommonArgExpansions; + computeCommonMacroArgExpansionFileIDs(Begin, End, SM, CommonArgExpansions); + Begin = retrieveMacroLocation(Begin, BeginFileID, CaretLocFileID, + CommonArgExpansions, /*IsBegin=*/true, SM); + End = retrieveMacroLocation(End, BeginFileID, CaretLocFileID, + CommonArgExpansions, /*IsBegin=*/false, SM); + if (Begin.isInvalid() || End.isInvalid()) continue; // 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)); } @@ -394,6 +457,96 @@ void DiagnosticRenderer::emitCaret(SourceLocation Loc, emitCodeContext(Loc, Level, SpellingRanges, Hints, SM); } +/// \brief A helper function for emitMacroExpansion to print the +/// macro expansion message +void DiagnosticRenderer::emitSingleMacroExpansion( + SourceLocation Loc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges, + const SourceManager &SM) { + // Find the spelling location for the macro definition. We must use the + // spelling location here to avoid emitting a macro backtrace for the note. + SourceLocation SpellingLoc = SM.getSpellingLoc(Loc); + + // 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); +} + +/// Check that the macro argument location of Loc starts with ArgumentLoc. +/// The starting location of the macro expansions is used to differeniate +/// different macro expansions. +static bool checkLocForMacroArgExpansion(SourceLocation Loc, + const SourceManager &SM, + SourceLocation ArgumentLoc) { + SourceLocation MacroLoc; + if (SM.isMacroArgExpansion(Loc, &MacroLoc)) { + if (ArgumentLoc == MacroLoc) return true; + } + + return false; +} + +/// Check if all the locations in the range have the same macro argument +/// expansion, and that that expansion starts with ArgumentLoc. +static bool checkRangeForMacroArgExpansion(CharSourceRange Range, + const SourceManager &SM, + SourceLocation ArgumentLoc) { + SourceLocation BegLoc = Range.getBegin(), EndLoc = Range.getEnd(); + while (BegLoc != EndLoc) { + if (!checkLocForMacroArgExpansion(BegLoc, SM, ArgumentLoc)) + return false; + BegLoc.getLocWithOffset(1); + } + + return checkLocForMacroArgExpansion(BegLoc, SM, ArgumentLoc); +} + +/// A helper function to check if the current ranges are all inside the same +/// macro argument expansion as Loc. +static bool checkRangesForMacroArgExpansion(SourceLocation Loc, + ArrayRef<CharSourceRange> Ranges, + const SourceManager &SM) { + assert(Loc.isMacroID() && "Must be a macro expansion!"); + + SmallVector<CharSourceRange, 4> SpellingRanges; + mapDiagnosticRanges(Loc, Ranges, SpellingRanges, &SM); + + /// Count all valid ranges. + unsigned ValidCount = 0; + for (auto I : Ranges) + if (I.isValid()) ValidCount++; + + if (ValidCount > SpellingRanges.size()) + return false; + + /// To store the source location of the argument location. + SourceLocation ArgumentLoc; + + /// Set the ArgumentLoc to the beginning location of the expansion of Loc + /// so to check if the ranges expands to the same beginning location. + if (!SM.isMacroArgExpansion(Loc,&ArgumentLoc)) + return false; + + for (auto I = SpellingRanges.begin(), E = SpellingRanges.end(); I != E; ++I) { + if (!checkRangeForMacroArgExpansion(*I, SM, ArgumentLoc)) + return false; + } + + return true; +} + /// \brief Recursively emit notes for each macro expansion and caret /// diagnostics where appropriate. /// @@ -405,71 +558,68 @@ void DiagnosticRenderer::emitCaret(SourceLocation Loc, /// \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; + const SourceManager &SM) { + assert(Loc.isValid() && "must have a valid source location here"); + + // Produce a stack of macro backtraces. + SmallVector<SourceLocation, 8> LocationStack; + unsigned IgnoredEnd = 0; + while (Loc.isMacroID()) { + // 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)) + LocationStack.push_back(SM.getImmediateExpansionRange(Loc).first); + else + LocationStack.push_back(Loc); + + if (checkRangesForMacroArgExpansion(Loc, Ranges, SM)) + IgnoredEnd = LocationStack.size(); + + Loc = SM.getImmediateMacroCallerLoc(Loc); + + // Once the location no longer points into a macro, try stepping through + // the last found location. This sometimes produces additional useful + // backtraces. + if (Loc.isFileID()) + Loc = SM.getImmediateMacroCallerLoc(LocationStack.back()); + assert(Loc.isValid() && "must have a valid source location here"); } - // 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()); - } + LocationStack.erase(LocationStack.begin(), + LocationStack.begin() + IgnoredEnd); + + unsigned MacroDepth = LocationStack.size(); + unsigned MacroLimit = DiagOpts->MacroBacktraceLimit; + if (MacroDepth <= MacroLimit || MacroLimit == 0) { + for (auto I = LocationStack.rbegin(), E = LocationStack.rend(); + I != E; ++I) + emitSingleMacroExpansion(*I, Level, Ranges, SM); 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); + unsigned MacroStartMessages = MacroLimit / 2; + unsigned MacroEndMessages = MacroLimit / 2 + MacroLimit % 2; - // Map the ranges into the FileID of the diagnostic location. - SmallVector<CharSourceRange, 4> SpellingRanges; - mapDiagnosticRanges(Loc, Ranges, SpellingRanges, &SM); + for (auto I = LocationStack.rbegin(), + E = LocationStack.rbegin() + MacroStartMessages; + I != E; ++I) + emitSingleMacroExpansion(*I, Level, Ranges, SM); - SmallString<100> MessageStorage; + SmallString<200> 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); + Message << "(skipping " << (MacroDepth - MacroLimit) + << " expansions in backtrace; use -fmacro-backtrace-limit=0 to " + "see all)"; + emitBasicNote(Message.str()); + + for (auto I = LocationStack.rend() - MacroEndMessages, + E = LocationStack.rend(); + I != E; ++I) + emitSingleMacroExpansion(*I, Level, Ranges, SM); } DiagnosticNoteRenderer::~DiagnosticNoteRenderer() {} @@ -492,8 +642,11 @@ void DiagnosticNoteRenderer::emitImportLocation(SourceLocation Loc, // 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() << ":"; + Message << "in module '" << ModuleName; + if (PLoc.isValid()) + Message << "' imported from " << PLoc.getFilename() << ':' + << PLoc.getLine(); + Message << ":"; emitNote(Loc, Message.str(), &SM); } |