xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/Frontend/DiagnosticRenderer.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //===- DiagnosticRenderer.cpp - Diagnostic Pretty-Printing ----------------===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg 
97330f729Sjoerg #include "clang/Frontend/DiagnosticRenderer.h"
107330f729Sjoerg #include "clang/Basic/Diagnostic.h"
117330f729Sjoerg #include "clang/Basic/DiagnosticOptions.h"
127330f729Sjoerg #include "clang/Basic/LLVM.h"
137330f729Sjoerg #include "clang/Basic/SourceLocation.h"
147330f729Sjoerg #include "clang/Basic/SourceManager.h"
157330f729Sjoerg #include "clang/Edit/Commit.h"
167330f729Sjoerg #include "clang/Edit/EditedSource.h"
177330f729Sjoerg #include "clang/Edit/EditsReceiver.h"
187330f729Sjoerg #include "clang/Lex/Lexer.h"
197330f729Sjoerg #include "llvm/ADT/ArrayRef.h"
207330f729Sjoerg #include "llvm/ADT/DenseMap.h"
217330f729Sjoerg #include "llvm/ADT/None.h"
227330f729Sjoerg #include "llvm/ADT/SmallString.h"
237330f729Sjoerg #include "llvm/ADT/SmallVector.h"
247330f729Sjoerg #include "llvm/ADT/StringRef.h"
257330f729Sjoerg #include "llvm/Support/raw_ostream.h"
267330f729Sjoerg #include <algorithm>
277330f729Sjoerg #include <cassert>
287330f729Sjoerg #include <iterator>
297330f729Sjoerg #include <utility>
307330f729Sjoerg 
317330f729Sjoerg using namespace clang;
327330f729Sjoerg 
DiagnosticRenderer(const LangOptions & LangOpts,DiagnosticOptions * DiagOpts)337330f729Sjoerg DiagnosticRenderer::DiagnosticRenderer(const LangOptions &LangOpts,
347330f729Sjoerg                                        DiagnosticOptions *DiagOpts)
357330f729Sjoerg     : LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {}
367330f729Sjoerg 
377330f729Sjoerg DiagnosticRenderer::~DiagnosticRenderer() = default;
387330f729Sjoerg 
397330f729Sjoerg namespace {
407330f729Sjoerg 
417330f729Sjoerg class FixitReceiver : public edit::EditsReceiver {
427330f729Sjoerg   SmallVectorImpl<FixItHint> &MergedFixits;
437330f729Sjoerg 
447330f729Sjoerg public:
FixitReceiver(SmallVectorImpl<FixItHint> & MergedFixits)457330f729Sjoerg   FixitReceiver(SmallVectorImpl<FixItHint> &MergedFixits)
467330f729Sjoerg       : MergedFixits(MergedFixits) {}
477330f729Sjoerg 
insert(SourceLocation loc,StringRef text)487330f729Sjoerg   void insert(SourceLocation loc, StringRef text) override {
497330f729Sjoerg     MergedFixits.push_back(FixItHint::CreateInsertion(loc, text));
507330f729Sjoerg   }
517330f729Sjoerg 
replace(CharSourceRange range,StringRef text)527330f729Sjoerg   void replace(CharSourceRange range, StringRef text) override {
537330f729Sjoerg     MergedFixits.push_back(FixItHint::CreateReplacement(range, text));
547330f729Sjoerg   }
557330f729Sjoerg };
567330f729Sjoerg 
577330f729Sjoerg } // namespace
587330f729Sjoerg 
mergeFixits(ArrayRef<FixItHint> FixItHints,const SourceManager & SM,const LangOptions & LangOpts,SmallVectorImpl<FixItHint> & MergedFixits)597330f729Sjoerg static void mergeFixits(ArrayRef<FixItHint> FixItHints,
607330f729Sjoerg                         const SourceManager &SM, const LangOptions &LangOpts,
617330f729Sjoerg                         SmallVectorImpl<FixItHint> &MergedFixits) {
627330f729Sjoerg   edit::Commit commit(SM, LangOpts);
637330f729Sjoerg   for (const auto &Hint : FixItHints)
647330f729Sjoerg     if (Hint.CodeToInsert.empty()) {
657330f729Sjoerg       if (Hint.InsertFromRange.isValid())
667330f729Sjoerg         commit.insertFromRange(Hint.RemoveRange.getBegin(),
677330f729Sjoerg                            Hint.InsertFromRange, /*afterToken=*/false,
687330f729Sjoerg                            Hint.BeforePreviousInsertions);
697330f729Sjoerg       else
707330f729Sjoerg         commit.remove(Hint.RemoveRange);
717330f729Sjoerg     } else {
727330f729Sjoerg       if (Hint.RemoveRange.isTokenRange() ||
737330f729Sjoerg           Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd())
747330f729Sjoerg         commit.replace(Hint.RemoveRange, Hint.CodeToInsert);
757330f729Sjoerg       else
767330f729Sjoerg         commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert,
777330f729Sjoerg                     /*afterToken=*/false, Hint.BeforePreviousInsertions);
787330f729Sjoerg     }
797330f729Sjoerg 
807330f729Sjoerg   edit::EditedSource Editor(SM, LangOpts);
817330f729Sjoerg   if (Editor.commit(commit)) {
827330f729Sjoerg     FixitReceiver Rec(MergedFixits);
837330f729Sjoerg     Editor.applyRewrites(Rec);
847330f729Sjoerg   }
857330f729Sjoerg }
867330f729Sjoerg 
emitDiagnostic(FullSourceLoc Loc,DiagnosticsEngine::Level Level,StringRef Message,ArrayRef<CharSourceRange> Ranges,ArrayRef<FixItHint> FixItHints,DiagOrStoredDiag D)877330f729Sjoerg void DiagnosticRenderer::emitDiagnostic(FullSourceLoc Loc,
887330f729Sjoerg                                         DiagnosticsEngine::Level Level,
897330f729Sjoerg                                         StringRef Message,
907330f729Sjoerg                                         ArrayRef<CharSourceRange> Ranges,
917330f729Sjoerg                                         ArrayRef<FixItHint> FixItHints,
927330f729Sjoerg                                         DiagOrStoredDiag D) {
937330f729Sjoerg   assert(Loc.hasManager() || Loc.isInvalid());
947330f729Sjoerg 
957330f729Sjoerg   beginDiagnostic(D, Level);
967330f729Sjoerg 
977330f729Sjoerg   if (!Loc.isValid())
987330f729Sjoerg     // If we have no source location, just emit the diagnostic message.
997330f729Sjoerg     emitDiagnosticMessage(Loc, PresumedLoc(), Level, Message, Ranges, D);
1007330f729Sjoerg   else {
1017330f729Sjoerg     // Get the ranges into a local array we can hack on.
1027330f729Sjoerg     SmallVector<CharSourceRange, 20> MutableRanges(Ranges.begin(),
1037330f729Sjoerg                                                    Ranges.end());
1047330f729Sjoerg 
1057330f729Sjoerg     SmallVector<FixItHint, 8> MergedFixits;
1067330f729Sjoerg     if (!FixItHints.empty()) {
1077330f729Sjoerg       mergeFixits(FixItHints, Loc.getManager(), LangOpts, MergedFixits);
1087330f729Sjoerg       FixItHints = MergedFixits;
1097330f729Sjoerg     }
1107330f729Sjoerg 
1117330f729Sjoerg     for (const auto &Hint : FixItHints)
1127330f729Sjoerg       if (Hint.RemoveRange.isValid())
1137330f729Sjoerg         MutableRanges.push_back(Hint.RemoveRange);
1147330f729Sjoerg 
1157330f729Sjoerg     FullSourceLoc UnexpandedLoc = Loc;
1167330f729Sjoerg 
1177330f729Sjoerg     // Find the ultimate expansion location for the diagnostic.
1187330f729Sjoerg     Loc = Loc.getFileLoc();
1197330f729Sjoerg 
1207330f729Sjoerg     PresumedLoc PLoc = Loc.getPresumedLoc(DiagOpts->ShowPresumedLoc);
1217330f729Sjoerg 
1227330f729Sjoerg     // First, if this diagnostic is not in the main file, print out the
1237330f729Sjoerg     // "included from" lines.
1247330f729Sjoerg     emitIncludeStack(Loc, PLoc, Level);
1257330f729Sjoerg 
1267330f729Sjoerg     // Next, emit the actual diagnostic message and caret.
1277330f729Sjoerg     emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, D);
1287330f729Sjoerg     emitCaret(Loc, Level, MutableRanges, FixItHints);
1297330f729Sjoerg 
1307330f729Sjoerg     // If this location is within a macro, walk from UnexpandedLoc up to Loc
1317330f729Sjoerg     // and produce a macro backtrace.
1327330f729Sjoerg     if (UnexpandedLoc.isValid() && UnexpandedLoc.isMacroID()) {
1337330f729Sjoerg       emitMacroExpansions(UnexpandedLoc, Level, MutableRanges, FixItHints);
1347330f729Sjoerg     }
1357330f729Sjoerg   }
1367330f729Sjoerg 
1377330f729Sjoerg   LastLoc = Loc;
1387330f729Sjoerg   LastLevel = Level;
1397330f729Sjoerg 
1407330f729Sjoerg   endDiagnostic(D, Level);
1417330f729Sjoerg }
1427330f729Sjoerg 
emitStoredDiagnostic(StoredDiagnostic & Diag)1437330f729Sjoerg void DiagnosticRenderer::emitStoredDiagnostic(StoredDiagnostic &Diag) {
1447330f729Sjoerg   emitDiagnostic(Diag.getLocation(), Diag.getLevel(), Diag.getMessage(),
1457330f729Sjoerg                  Diag.getRanges(), Diag.getFixIts(),
1467330f729Sjoerg                  &Diag);
1477330f729Sjoerg }
1487330f729Sjoerg 
emitBasicNote(StringRef Message)1497330f729Sjoerg void DiagnosticRenderer::emitBasicNote(StringRef Message) {
1507330f729Sjoerg   emitDiagnosticMessage(FullSourceLoc(), PresumedLoc(), DiagnosticsEngine::Note,
1517330f729Sjoerg                         Message, None, DiagOrStoredDiag());
1527330f729Sjoerg }
1537330f729Sjoerg 
1547330f729Sjoerg /// Prints an include stack when appropriate for a particular
1557330f729Sjoerg /// diagnostic level and location.
1567330f729Sjoerg ///
1577330f729Sjoerg /// This routine handles all the logic of suppressing particular include
1587330f729Sjoerg /// stacks (such as those for notes) and duplicate include stacks when
1597330f729Sjoerg /// repeated warnings occur within the same file. It also handles the logic
1607330f729Sjoerg /// of customizing the formatting and display of the include stack.
1617330f729Sjoerg ///
1627330f729Sjoerg /// \param Loc   The diagnostic location.
1637330f729Sjoerg /// \param PLoc  The presumed location of the diagnostic location.
1647330f729Sjoerg /// \param Level The diagnostic level of the message this stack pertains to.
emitIncludeStack(FullSourceLoc Loc,PresumedLoc PLoc,DiagnosticsEngine::Level Level)1657330f729Sjoerg void DiagnosticRenderer::emitIncludeStack(FullSourceLoc Loc, PresumedLoc PLoc,
1667330f729Sjoerg                                           DiagnosticsEngine::Level Level) {
1677330f729Sjoerg   FullSourceLoc IncludeLoc =
1687330f729Sjoerg       PLoc.isInvalid() ? FullSourceLoc()
1697330f729Sjoerg                        : FullSourceLoc(PLoc.getIncludeLoc(), Loc.getManager());
1707330f729Sjoerg 
1717330f729Sjoerg   // Skip redundant include stacks altogether.
1727330f729Sjoerg   if (LastIncludeLoc == IncludeLoc)
1737330f729Sjoerg     return;
1747330f729Sjoerg 
1757330f729Sjoerg   LastIncludeLoc = IncludeLoc;
1767330f729Sjoerg 
1777330f729Sjoerg   if (!DiagOpts->ShowNoteIncludeStack && Level == DiagnosticsEngine::Note)
1787330f729Sjoerg     return;
1797330f729Sjoerg 
1807330f729Sjoerg   if (IncludeLoc.isValid())
1817330f729Sjoerg     emitIncludeStackRecursively(IncludeLoc);
1827330f729Sjoerg   else {
1837330f729Sjoerg     emitModuleBuildStack(Loc.getManager());
1847330f729Sjoerg     emitImportStack(Loc);
1857330f729Sjoerg   }
1867330f729Sjoerg }
1877330f729Sjoerg 
1887330f729Sjoerg /// Helper to recursively walk up the include stack and print each layer
1897330f729Sjoerg /// on the way back down.
emitIncludeStackRecursively(FullSourceLoc Loc)1907330f729Sjoerg void DiagnosticRenderer::emitIncludeStackRecursively(FullSourceLoc Loc) {
1917330f729Sjoerg   if (Loc.isInvalid()) {
1927330f729Sjoerg     emitModuleBuildStack(Loc.getManager());
1937330f729Sjoerg     return;
1947330f729Sjoerg   }
1957330f729Sjoerg 
1967330f729Sjoerg   PresumedLoc PLoc = Loc.getPresumedLoc(DiagOpts->ShowPresumedLoc);
1977330f729Sjoerg   if (PLoc.isInvalid())
1987330f729Sjoerg     return;
1997330f729Sjoerg 
2007330f729Sjoerg   // If this source location was imported from a module, print the module
2017330f729Sjoerg   // import stack rather than the
2027330f729Sjoerg   // FIXME: We want submodule granularity here.
2037330f729Sjoerg   std::pair<FullSourceLoc, StringRef> Imported = Loc.getModuleImportLoc();
2047330f729Sjoerg   if (!Imported.second.empty()) {
2057330f729Sjoerg     // This location was imported by a module. Emit the module import stack.
2067330f729Sjoerg     emitImportStackRecursively(Imported.first, Imported.second);
2077330f729Sjoerg     return;
2087330f729Sjoerg   }
2097330f729Sjoerg 
2107330f729Sjoerg   // Emit the other include frames first.
2117330f729Sjoerg   emitIncludeStackRecursively(
2127330f729Sjoerg       FullSourceLoc(PLoc.getIncludeLoc(), Loc.getManager()));
2137330f729Sjoerg 
2147330f729Sjoerg   // Emit the inclusion text/note.
2157330f729Sjoerg   emitIncludeLocation(Loc, PLoc);
2167330f729Sjoerg }
2177330f729Sjoerg 
2187330f729Sjoerg /// Emit the module import stack associated with the current location.
emitImportStack(FullSourceLoc Loc)2197330f729Sjoerg void DiagnosticRenderer::emitImportStack(FullSourceLoc Loc) {
2207330f729Sjoerg   if (Loc.isInvalid()) {
2217330f729Sjoerg     emitModuleBuildStack(Loc.getManager());
2227330f729Sjoerg     return;
2237330f729Sjoerg   }
2247330f729Sjoerg 
2257330f729Sjoerg   std::pair<FullSourceLoc, StringRef> NextImportLoc = Loc.getModuleImportLoc();
2267330f729Sjoerg   emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second);
2277330f729Sjoerg }
2287330f729Sjoerg 
2297330f729Sjoerg /// Helper to recursively walk up the import stack and print each layer
2307330f729Sjoerg /// on the way back down.
emitImportStackRecursively(FullSourceLoc Loc,StringRef ModuleName)2317330f729Sjoerg void DiagnosticRenderer::emitImportStackRecursively(FullSourceLoc Loc,
2327330f729Sjoerg                                                     StringRef ModuleName) {
2337330f729Sjoerg   if (ModuleName.empty()) {
2347330f729Sjoerg     return;
2357330f729Sjoerg   }
2367330f729Sjoerg 
2377330f729Sjoerg   PresumedLoc PLoc = Loc.getPresumedLoc(DiagOpts->ShowPresumedLoc);
2387330f729Sjoerg 
2397330f729Sjoerg   // Emit the other import frames first.
2407330f729Sjoerg   std::pair<FullSourceLoc, StringRef> NextImportLoc = Loc.getModuleImportLoc();
2417330f729Sjoerg   emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second);
2427330f729Sjoerg 
2437330f729Sjoerg   // Emit the inclusion text/note.
2447330f729Sjoerg   emitImportLocation(Loc, PLoc, ModuleName);
2457330f729Sjoerg }
2467330f729Sjoerg 
2477330f729Sjoerg /// Emit the module build stack, for cases where a module is (re-)built
2487330f729Sjoerg /// on demand.
emitModuleBuildStack(const SourceManager & SM)2497330f729Sjoerg void DiagnosticRenderer::emitModuleBuildStack(const SourceManager &SM) {
2507330f729Sjoerg   ModuleBuildStack Stack = SM.getModuleBuildStack();
2517330f729Sjoerg   for (const auto &I : Stack) {
2527330f729Sjoerg     emitBuildingModuleLocation(I.second, I.second.getPresumedLoc(
2537330f729Sjoerg                                               DiagOpts->ShowPresumedLoc),
2547330f729Sjoerg                                I.first);
2557330f729Sjoerg   }
2567330f729Sjoerg }
2577330f729Sjoerg 
2587330f729Sjoerg /// A recursive function to trace all possible backtrace locations
2597330f729Sjoerg /// to match the \p CaretLocFileID.
2607330f729Sjoerg static SourceLocation
retrieveMacroLocation(SourceLocation Loc,FileID MacroFileID,FileID CaretFileID,const SmallVectorImpl<FileID> & CommonArgExpansions,bool IsBegin,const SourceManager * SM,bool & IsTokenRange)2617330f729Sjoerg retrieveMacroLocation(SourceLocation Loc, FileID MacroFileID,
2627330f729Sjoerg                       FileID CaretFileID,
2637330f729Sjoerg                       const SmallVectorImpl<FileID> &CommonArgExpansions,
2647330f729Sjoerg                       bool IsBegin, const SourceManager *SM,
2657330f729Sjoerg                       bool &IsTokenRange) {
2667330f729Sjoerg   assert(SM->getFileID(Loc) == MacroFileID);
2677330f729Sjoerg   if (MacroFileID == CaretFileID)
2687330f729Sjoerg     return Loc;
2697330f729Sjoerg   if (!Loc.isMacroID())
2707330f729Sjoerg     return {};
2717330f729Sjoerg 
2727330f729Sjoerg   CharSourceRange MacroRange, MacroArgRange;
2737330f729Sjoerg 
2747330f729Sjoerg   if (SM->isMacroArgExpansion(Loc)) {
2757330f729Sjoerg     // Only look at the immediate spelling location of this macro argument if
2767330f729Sjoerg     // the other location in the source range is also present in that expansion.
2777330f729Sjoerg     if (std::binary_search(CommonArgExpansions.begin(),
2787330f729Sjoerg                            CommonArgExpansions.end(), MacroFileID))
2797330f729Sjoerg       MacroRange =
2807330f729Sjoerg           CharSourceRange(SM->getImmediateSpellingLoc(Loc), IsTokenRange);
2817330f729Sjoerg     MacroArgRange = SM->getImmediateExpansionRange(Loc);
2827330f729Sjoerg   } else {
2837330f729Sjoerg     MacroRange = SM->getImmediateExpansionRange(Loc);
2847330f729Sjoerg     MacroArgRange =
2857330f729Sjoerg         CharSourceRange(SM->getImmediateSpellingLoc(Loc), IsTokenRange);
2867330f729Sjoerg   }
2877330f729Sjoerg 
2887330f729Sjoerg   SourceLocation MacroLocation =
2897330f729Sjoerg       IsBegin ? MacroRange.getBegin() : MacroRange.getEnd();
2907330f729Sjoerg   if (MacroLocation.isValid()) {
2917330f729Sjoerg     MacroFileID = SM->getFileID(MacroLocation);
2927330f729Sjoerg     bool TokenRange = IsBegin ? IsTokenRange : MacroRange.isTokenRange();
2937330f729Sjoerg     MacroLocation =
2947330f729Sjoerg         retrieveMacroLocation(MacroLocation, MacroFileID, CaretFileID,
2957330f729Sjoerg                               CommonArgExpansions, IsBegin, SM, TokenRange);
2967330f729Sjoerg     if (MacroLocation.isValid()) {
2977330f729Sjoerg       IsTokenRange = TokenRange;
2987330f729Sjoerg       return MacroLocation;
2997330f729Sjoerg     }
3007330f729Sjoerg   }
3017330f729Sjoerg 
3027330f729Sjoerg   // If we moved the end of the range to an expansion location, we now have
3037330f729Sjoerg   // a range of the same kind as the expansion range.
3047330f729Sjoerg   if (!IsBegin)
3057330f729Sjoerg     IsTokenRange = MacroArgRange.isTokenRange();
3067330f729Sjoerg 
3077330f729Sjoerg   SourceLocation MacroArgLocation =
3087330f729Sjoerg       IsBegin ? MacroArgRange.getBegin() : MacroArgRange.getEnd();
3097330f729Sjoerg   MacroFileID = SM->getFileID(MacroArgLocation);
3107330f729Sjoerg   return retrieveMacroLocation(MacroArgLocation, MacroFileID, CaretFileID,
3117330f729Sjoerg                                CommonArgExpansions, IsBegin, SM, IsTokenRange);
3127330f729Sjoerg }
3137330f729Sjoerg 
3147330f729Sjoerg /// Walk up the chain of macro expansions and collect the FileIDs identifying the
3157330f729Sjoerg /// expansions.
getMacroArgExpansionFileIDs(SourceLocation Loc,SmallVectorImpl<FileID> & IDs,bool IsBegin,const SourceManager * SM)3167330f729Sjoerg static void getMacroArgExpansionFileIDs(SourceLocation Loc,
3177330f729Sjoerg                                         SmallVectorImpl<FileID> &IDs,
3187330f729Sjoerg                                         bool IsBegin, const SourceManager *SM) {
3197330f729Sjoerg   while (Loc.isMacroID()) {
3207330f729Sjoerg     if (SM->isMacroArgExpansion(Loc)) {
3217330f729Sjoerg       IDs.push_back(SM->getFileID(Loc));
3227330f729Sjoerg       Loc = SM->getImmediateSpellingLoc(Loc);
3237330f729Sjoerg     } else {
3247330f729Sjoerg       auto ExpRange = SM->getImmediateExpansionRange(Loc);
3257330f729Sjoerg       Loc = IsBegin ? ExpRange.getBegin() : ExpRange.getEnd();
3267330f729Sjoerg     }
3277330f729Sjoerg   }
3287330f729Sjoerg }
3297330f729Sjoerg 
3307330f729Sjoerg /// Collect the expansions of the begin and end locations and compute the set
3317330f729Sjoerg /// intersection. Produces a sorted vector of FileIDs in CommonArgExpansions.
computeCommonMacroArgExpansionFileIDs(SourceLocation Begin,SourceLocation End,const SourceManager * SM,SmallVectorImpl<FileID> & CommonArgExpansions)3327330f729Sjoerg static void computeCommonMacroArgExpansionFileIDs(
3337330f729Sjoerg     SourceLocation Begin, SourceLocation End, const SourceManager *SM,
3347330f729Sjoerg     SmallVectorImpl<FileID> &CommonArgExpansions) {
3357330f729Sjoerg   SmallVector<FileID, 4> BeginArgExpansions;
3367330f729Sjoerg   SmallVector<FileID, 4> EndArgExpansions;
3377330f729Sjoerg   getMacroArgExpansionFileIDs(Begin, BeginArgExpansions, /*IsBegin=*/true, SM);
3387330f729Sjoerg   getMacroArgExpansionFileIDs(End, EndArgExpansions, /*IsBegin=*/false, SM);
3397330f729Sjoerg   llvm::sort(BeginArgExpansions);
3407330f729Sjoerg   llvm::sort(EndArgExpansions);
3417330f729Sjoerg   std::set_intersection(BeginArgExpansions.begin(), BeginArgExpansions.end(),
3427330f729Sjoerg                         EndArgExpansions.begin(), EndArgExpansions.end(),
3437330f729Sjoerg                         std::back_inserter(CommonArgExpansions));
3447330f729Sjoerg }
3457330f729Sjoerg 
3467330f729Sjoerg // Helper function to fix up source ranges.  It takes in an array of ranges,
3477330f729Sjoerg // and outputs an array of ranges where we want to draw the range highlighting
3487330f729Sjoerg // around the location specified by CaretLoc.
3497330f729Sjoerg //
3507330f729Sjoerg // To find locations which correspond to the caret, we crawl the macro caller
3517330f729Sjoerg // chain for the beginning and end of each range.  If the caret location
3527330f729Sjoerg // is in a macro expansion, we search each chain for a location
3537330f729Sjoerg // in the same expansion as the caret; otherwise, we crawl to the top of
3547330f729Sjoerg // each chain. Two locations are part of the same macro expansion
3557330f729Sjoerg // iff the FileID is the same.
3567330f729Sjoerg static void
mapDiagnosticRanges(FullSourceLoc CaretLoc,ArrayRef<CharSourceRange> Ranges,SmallVectorImpl<CharSourceRange> & SpellingRanges)3577330f729Sjoerg mapDiagnosticRanges(FullSourceLoc CaretLoc, ArrayRef<CharSourceRange> Ranges,
3587330f729Sjoerg                     SmallVectorImpl<CharSourceRange> &SpellingRanges) {
3597330f729Sjoerg   FileID CaretLocFileID = CaretLoc.getFileID();
3607330f729Sjoerg 
3617330f729Sjoerg   const SourceManager *SM = &CaretLoc.getManager();
3627330f729Sjoerg 
3637330f729Sjoerg   for (const auto &Range : Ranges) {
3647330f729Sjoerg     if (Range.isInvalid())
3657330f729Sjoerg       continue;
3667330f729Sjoerg 
3677330f729Sjoerg     SourceLocation Begin = Range.getBegin(), End = Range.getEnd();
3687330f729Sjoerg     bool IsTokenRange = Range.isTokenRange();
3697330f729Sjoerg 
3707330f729Sjoerg     FileID BeginFileID = SM->getFileID(Begin);
3717330f729Sjoerg     FileID EndFileID = SM->getFileID(End);
3727330f729Sjoerg 
3737330f729Sjoerg     // Find the common parent for the beginning and end of the range.
3747330f729Sjoerg 
3757330f729Sjoerg     // First, crawl the expansion chain for the beginning of the range.
3767330f729Sjoerg     llvm::SmallDenseMap<FileID, SourceLocation> BeginLocsMap;
3777330f729Sjoerg     while (Begin.isMacroID() && BeginFileID != EndFileID) {
3787330f729Sjoerg       BeginLocsMap[BeginFileID] = Begin;
3797330f729Sjoerg       Begin = SM->getImmediateExpansionRange(Begin).getBegin();
3807330f729Sjoerg       BeginFileID = SM->getFileID(Begin);
3817330f729Sjoerg     }
3827330f729Sjoerg 
3837330f729Sjoerg     // Then, crawl the expansion chain for the end of the range.
3847330f729Sjoerg     if (BeginFileID != EndFileID) {
3857330f729Sjoerg       while (End.isMacroID() && !BeginLocsMap.count(EndFileID)) {
3867330f729Sjoerg         auto Exp = SM->getImmediateExpansionRange(End);
3877330f729Sjoerg         IsTokenRange = Exp.isTokenRange();
3887330f729Sjoerg         End = Exp.getEnd();
3897330f729Sjoerg         EndFileID = SM->getFileID(End);
3907330f729Sjoerg       }
3917330f729Sjoerg       if (End.isMacroID()) {
3927330f729Sjoerg         Begin = BeginLocsMap[EndFileID];
3937330f729Sjoerg         BeginFileID = EndFileID;
3947330f729Sjoerg       }
3957330f729Sjoerg     }
3967330f729Sjoerg 
397*e038c9c4Sjoerg     // There is a chance that begin or end is invalid here, for example if
398*e038c9c4Sjoerg     // specific compile error is reported.
399*e038c9c4Sjoerg     // It is possible that the FileID's do not match, if one comes from an
400*e038c9c4Sjoerg     // included file. In this case we can not produce a meaningful source range.
401*e038c9c4Sjoerg     if (Begin.isInvalid() || End.isInvalid() || BeginFileID != EndFileID)
402*e038c9c4Sjoerg       continue;
403*e038c9c4Sjoerg 
4047330f729Sjoerg     // Do the backtracking.
4057330f729Sjoerg     SmallVector<FileID, 4> CommonArgExpansions;
4067330f729Sjoerg     computeCommonMacroArgExpansionFileIDs(Begin, End, SM, CommonArgExpansions);
4077330f729Sjoerg     Begin = retrieveMacroLocation(Begin, BeginFileID, CaretLocFileID,
4087330f729Sjoerg                                   CommonArgExpansions, /*IsBegin=*/true, SM,
4097330f729Sjoerg                                   IsTokenRange);
4107330f729Sjoerg     End = retrieveMacroLocation(End, BeginFileID, CaretLocFileID,
4117330f729Sjoerg                                 CommonArgExpansions, /*IsBegin=*/false, SM,
4127330f729Sjoerg                                 IsTokenRange);
4137330f729Sjoerg     if (Begin.isInvalid() || End.isInvalid()) continue;
4147330f729Sjoerg 
4157330f729Sjoerg     // Return the spelling location of the beginning and end of the range.
4167330f729Sjoerg     Begin = SM->getSpellingLoc(Begin);
4177330f729Sjoerg     End = SM->getSpellingLoc(End);
4187330f729Sjoerg 
4197330f729Sjoerg     SpellingRanges.push_back(CharSourceRange(SourceRange(Begin, End),
4207330f729Sjoerg                                              IsTokenRange));
4217330f729Sjoerg   }
4227330f729Sjoerg }
4237330f729Sjoerg 
emitCaret(FullSourceLoc Loc,DiagnosticsEngine::Level Level,ArrayRef<CharSourceRange> Ranges,ArrayRef<FixItHint> Hints)4247330f729Sjoerg void DiagnosticRenderer::emitCaret(FullSourceLoc Loc,
4257330f729Sjoerg                                    DiagnosticsEngine::Level Level,
4267330f729Sjoerg                                    ArrayRef<CharSourceRange> Ranges,
4277330f729Sjoerg                                    ArrayRef<FixItHint> Hints) {
4287330f729Sjoerg   SmallVector<CharSourceRange, 4> SpellingRanges;
4297330f729Sjoerg   mapDiagnosticRanges(Loc, Ranges, SpellingRanges);
4307330f729Sjoerg   emitCodeContext(Loc, Level, SpellingRanges, Hints);
4317330f729Sjoerg }
4327330f729Sjoerg 
4337330f729Sjoerg /// A helper function for emitMacroExpansion to print the
4347330f729Sjoerg /// macro expansion message
emitSingleMacroExpansion(FullSourceLoc Loc,DiagnosticsEngine::Level Level,ArrayRef<CharSourceRange> Ranges)4357330f729Sjoerg void DiagnosticRenderer::emitSingleMacroExpansion(
4367330f729Sjoerg     FullSourceLoc Loc, DiagnosticsEngine::Level Level,
4377330f729Sjoerg     ArrayRef<CharSourceRange> Ranges) {
4387330f729Sjoerg   // Find the spelling location for the macro definition. We must use the
4397330f729Sjoerg   // spelling location here to avoid emitting a macro backtrace for the note.
4407330f729Sjoerg   FullSourceLoc SpellingLoc = Loc.getSpellingLoc();
4417330f729Sjoerg 
4427330f729Sjoerg   // Map the ranges into the FileID of the diagnostic location.
4437330f729Sjoerg   SmallVector<CharSourceRange, 4> SpellingRanges;
4447330f729Sjoerg   mapDiagnosticRanges(Loc, Ranges, SpellingRanges);
4457330f729Sjoerg 
4467330f729Sjoerg   SmallString<100> MessageStorage;
4477330f729Sjoerg   llvm::raw_svector_ostream Message(MessageStorage);
4487330f729Sjoerg   StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics(
4497330f729Sjoerg       Loc, Loc.getManager(), LangOpts);
4507330f729Sjoerg   if (MacroName.empty())
4517330f729Sjoerg     Message << "expanded from here";
4527330f729Sjoerg   else
4537330f729Sjoerg     Message << "expanded from macro '" << MacroName << "'";
4547330f729Sjoerg 
4557330f729Sjoerg   emitDiagnostic(SpellingLoc, DiagnosticsEngine::Note, Message.str(),
4567330f729Sjoerg                  SpellingRanges, None);
4577330f729Sjoerg }
4587330f729Sjoerg 
4597330f729Sjoerg /// Check that the macro argument location of Loc starts with ArgumentLoc.
4607330f729Sjoerg /// The starting location of the macro expansions is used to differeniate
4617330f729Sjoerg /// different macro expansions.
checkLocForMacroArgExpansion(SourceLocation Loc,const SourceManager & SM,SourceLocation ArgumentLoc)4627330f729Sjoerg static bool checkLocForMacroArgExpansion(SourceLocation Loc,
4637330f729Sjoerg                                          const SourceManager &SM,
4647330f729Sjoerg                                          SourceLocation ArgumentLoc) {
4657330f729Sjoerg   SourceLocation MacroLoc;
4667330f729Sjoerg   if (SM.isMacroArgExpansion(Loc, &MacroLoc)) {
4677330f729Sjoerg     if (ArgumentLoc == MacroLoc) return true;
4687330f729Sjoerg   }
4697330f729Sjoerg 
4707330f729Sjoerg   return false;
4717330f729Sjoerg }
4727330f729Sjoerg 
4737330f729Sjoerg /// Check if all the locations in the range have the same macro argument
4747330f729Sjoerg /// expansion, and that the expansion starts with ArgumentLoc.
checkRangeForMacroArgExpansion(CharSourceRange Range,const SourceManager & SM,SourceLocation ArgumentLoc)4757330f729Sjoerg static bool checkRangeForMacroArgExpansion(CharSourceRange Range,
4767330f729Sjoerg                                            const SourceManager &SM,
4777330f729Sjoerg                                            SourceLocation ArgumentLoc) {
4787330f729Sjoerg   SourceLocation BegLoc = Range.getBegin(), EndLoc = Range.getEnd();
4797330f729Sjoerg   while (BegLoc != EndLoc) {
4807330f729Sjoerg     if (!checkLocForMacroArgExpansion(BegLoc, SM, ArgumentLoc))
4817330f729Sjoerg       return false;
4827330f729Sjoerg     BegLoc.getLocWithOffset(1);
4837330f729Sjoerg   }
4847330f729Sjoerg 
4857330f729Sjoerg   return checkLocForMacroArgExpansion(BegLoc, SM, ArgumentLoc);
4867330f729Sjoerg }
4877330f729Sjoerg 
4887330f729Sjoerg /// A helper function to check if the current ranges are all inside the same
4897330f729Sjoerg /// macro argument expansion as Loc.
checkRangesForMacroArgExpansion(FullSourceLoc Loc,ArrayRef<CharSourceRange> Ranges)4907330f729Sjoerg static bool checkRangesForMacroArgExpansion(FullSourceLoc Loc,
4917330f729Sjoerg                                             ArrayRef<CharSourceRange> Ranges) {
4927330f729Sjoerg   assert(Loc.isMacroID() && "Must be a macro expansion!");
4937330f729Sjoerg 
4947330f729Sjoerg   SmallVector<CharSourceRange, 4> SpellingRanges;
4957330f729Sjoerg   mapDiagnosticRanges(Loc, Ranges, SpellingRanges);
4967330f729Sjoerg 
4977330f729Sjoerg   /// Count all valid ranges.
4987330f729Sjoerg   unsigned ValidCount = 0;
4997330f729Sjoerg   for (const auto &Range : Ranges)
5007330f729Sjoerg     if (Range.isValid())
5017330f729Sjoerg       ValidCount++;
5027330f729Sjoerg 
5037330f729Sjoerg   if (ValidCount > SpellingRanges.size())
5047330f729Sjoerg     return false;
5057330f729Sjoerg 
5067330f729Sjoerg   /// To store the source location of the argument location.
5077330f729Sjoerg   FullSourceLoc ArgumentLoc;
5087330f729Sjoerg 
5097330f729Sjoerg   /// Set the ArgumentLoc to the beginning location of the expansion of Loc
5107330f729Sjoerg   /// so to check if the ranges expands to the same beginning location.
5117330f729Sjoerg   if (!Loc.isMacroArgExpansion(&ArgumentLoc))
5127330f729Sjoerg     return false;
5137330f729Sjoerg 
5147330f729Sjoerg   for (const auto &Range : SpellingRanges)
5157330f729Sjoerg     if (!checkRangeForMacroArgExpansion(Range, Loc.getManager(), ArgumentLoc))
5167330f729Sjoerg       return false;
5177330f729Sjoerg 
5187330f729Sjoerg   return true;
5197330f729Sjoerg }
5207330f729Sjoerg 
5217330f729Sjoerg /// Recursively emit notes for each macro expansion and caret
5227330f729Sjoerg /// diagnostics where appropriate.
5237330f729Sjoerg ///
5247330f729Sjoerg /// Walks up the macro expansion stack printing expansion notes, the code
5257330f729Sjoerg /// snippet, caret, underlines and FixItHint display as appropriate at each
5267330f729Sjoerg /// level.
5277330f729Sjoerg ///
5287330f729Sjoerg /// \param Loc The location for this caret.
5297330f729Sjoerg /// \param Level The diagnostic level currently being emitted.
5307330f729Sjoerg /// \param Ranges The underlined ranges for this code snippet.
5317330f729Sjoerg /// \param Hints The FixIt hints active for this diagnostic.
emitMacroExpansions(FullSourceLoc Loc,DiagnosticsEngine::Level Level,ArrayRef<CharSourceRange> Ranges,ArrayRef<FixItHint> Hints)5327330f729Sjoerg void DiagnosticRenderer::emitMacroExpansions(FullSourceLoc Loc,
5337330f729Sjoerg                                              DiagnosticsEngine::Level Level,
5347330f729Sjoerg                                              ArrayRef<CharSourceRange> Ranges,
5357330f729Sjoerg                                              ArrayRef<FixItHint> Hints) {
5367330f729Sjoerg   assert(Loc.isValid() && "must have a valid source location here");
5377330f729Sjoerg   const SourceManager &SM = Loc.getManager();
5387330f729Sjoerg   SourceLocation L = Loc;
5397330f729Sjoerg 
5407330f729Sjoerg   // Produce a stack of macro backtraces.
5417330f729Sjoerg   SmallVector<SourceLocation, 8> LocationStack;
5427330f729Sjoerg   unsigned IgnoredEnd = 0;
5437330f729Sjoerg   while (L.isMacroID()) {
5447330f729Sjoerg     // If this is the expansion of a macro argument, point the caret at the
5457330f729Sjoerg     // use of the argument in the definition of the macro, not the expansion.
5467330f729Sjoerg     if (SM.isMacroArgExpansion(L))
5477330f729Sjoerg       LocationStack.push_back(SM.getImmediateExpansionRange(L).getBegin());
5487330f729Sjoerg     else
5497330f729Sjoerg       LocationStack.push_back(L);
5507330f729Sjoerg 
5517330f729Sjoerg     if (checkRangesForMacroArgExpansion(FullSourceLoc(L, SM), Ranges))
5527330f729Sjoerg       IgnoredEnd = LocationStack.size();
5537330f729Sjoerg 
5547330f729Sjoerg     L = SM.getImmediateMacroCallerLoc(L);
5557330f729Sjoerg 
5567330f729Sjoerg     // Once the location no longer points into a macro, try stepping through
5577330f729Sjoerg     // the last found location.  This sometimes produces additional useful
5587330f729Sjoerg     // backtraces.
5597330f729Sjoerg     if (L.isFileID())
5607330f729Sjoerg       L = SM.getImmediateMacroCallerLoc(LocationStack.back());
5617330f729Sjoerg     assert(L.isValid() && "must have a valid source location here");
5627330f729Sjoerg   }
5637330f729Sjoerg 
5647330f729Sjoerg   LocationStack.erase(LocationStack.begin(),
5657330f729Sjoerg                       LocationStack.begin() + IgnoredEnd);
5667330f729Sjoerg 
5677330f729Sjoerg   unsigned MacroDepth = LocationStack.size();
5687330f729Sjoerg   unsigned MacroLimit = DiagOpts->MacroBacktraceLimit;
5697330f729Sjoerg   if (MacroDepth <= MacroLimit || MacroLimit == 0) {
5707330f729Sjoerg     for (auto I = LocationStack.rbegin(), E = LocationStack.rend();
5717330f729Sjoerg          I != E; ++I)
5727330f729Sjoerg       emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges);
5737330f729Sjoerg     return;
5747330f729Sjoerg   }
5757330f729Sjoerg 
5767330f729Sjoerg   unsigned MacroStartMessages = MacroLimit / 2;
5777330f729Sjoerg   unsigned MacroEndMessages = MacroLimit / 2 + MacroLimit % 2;
5787330f729Sjoerg 
5797330f729Sjoerg   for (auto I = LocationStack.rbegin(),
5807330f729Sjoerg             E = LocationStack.rbegin() + MacroStartMessages;
5817330f729Sjoerg        I != E; ++I)
5827330f729Sjoerg     emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges);
5837330f729Sjoerg 
5847330f729Sjoerg   SmallString<200> MessageStorage;
5857330f729Sjoerg   llvm::raw_svector_ostream Message(MessageStorage);
5867330f729Sjoerg   Message << "(skipping " << (MacroDepth - MacroLimit)
5877330f729Sjoerg           << " expansions in backtrace; use -fmacro-backtrace-limit=0 to "
5887330f729Sjoerg              "see all)";
5897330f729Sjoerg   emitBasicNote(Message.str());
5907330f729Sjoerg 
5917330f729Sjoerg   for (auto I = LocationStack.rend() - MacroEndMessages,
5927330f729Sjoerg             E = LocationStack.rend();
5937330f729Sjoerg        I != E; ++I)
5947330f729Sjoerg     emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges);
5957330f729Sjoerg }
5967330f729Sjoerg 
5977330f729Sjoerg DiagnosticNoteRenderer::~DiagnosticNoteRenderer() = default;
5987330f729Sjoerg 
emitIncludeLocation(FullSourceLoc Loc,PresumedLoc PLoc)5997330f729Sjoerg void DiagnosticNoteRenderer::emitIncludeLocation(FullSourceLoc Loc,
6007330f729Sjoerg                                                  PresumedLoc PLoc) {
6017330f729Sjoerg   // Generate a note indicating the include location.
6027330f729Sjoerg   SmallString<200> MessageStorage;
6037330f729Sjoerg   llvm::raw_svector_ostream Message(MessageStorage);
6047330f729Sjoerg   Message << "in file included from " << PLoc.getFilename() << ':'
6057330f729Sjoerg           << PLoc.getLine() << ":";
6067330f729Sjoerg   emitNote(Loc, Message.str());
6077330f729Sjoerg }
6087330f729Sjoerg 
emitImportLocation(FullSourceLoc Loc,PresumedLoc PLoc,StringRef ModuleName)6097330f729Sjoerg void DiagnosticNoteRenderer::emitImportLocation(FullSourceLoc Loc,
6107330f729Sjoerg                                                 PresumedLoc PLoc,
6117330f729Sjoerg                                                 StringRef ModuleName) {
6127330f729Sjoerg   // Generate a note indicating the include location.
6137330f729Sjoerg   SmallString<200> MessageStorage;
6147330f729Sjoerg   llvm::raw_svector_ostream Message(MessageStorage);
6157330f729Sjoerg   Message << "in module '" << ModuleName;
6167330f729Sjoerg   if (PLoc.isValid())
6177330f729Sjoerg     Message << "' imported from " << PLoc.getFilename() << ':'
6187330f729Sjoerg             << PLoc.getLine();
6197330f729Sjoerg   Message << ":";
6207330f729Sjoerg   emitNote(Loc, Message.str());
6217330f729Sjoerg }
6227330f729Sjoerg 
emitBuildingModuleLocation(FullSourceLoc Loc,PresumedLoc PLoc,StringRef ModuleName)6237330f729Sjoerg void DiagnosticNoteRenderer::emitBuildingModuleLocation(FullSourceLoc Loc,
6247330f729Sjoerg                                                         PresumedLoc PLoc,
6257330f729Sjoerg                                                         StringRef ModuleName) {
6267330f729Sjoerg   // Generate a note indicating the include location.
6277330f729Sjoerg   SmallString<200> MessageStorage;
6287330f729Sjoerg   llvm::raw_svector_ostream Message(MessageStorage);
6297330f729Sjoerg   if (PLoc.isValid())
6307330f729Sjoerg     Message << "while building module '" << ModuleName << "' imported from "
6317330f729Sjoerg             << PLoc.getFilename() << ':' << PLoc.getLine() << ":";
6327330f729Sjoerg   else
6337330f729Sjoerg     Message << "while building module '" << ModuleName << "':";
6347330f729Sjoerg   emitNote(Loc, Message.str());
6357330f729Sjoerg }
636