xref: /openbsd-src/gnu/llvm/clang/lib/Frontend/DiagnosticRenderer.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1e5dd7070Spatrick //===- DiagnosticRenderer.cpp - Diagnostic Pretty-Printing ----------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick 
9e5dd7070Spatrick #include "clang/Frontend/DiagnosticRenderer.h"
10e5dd7070Spatrick #include "clang/Basic/Diagnostic.h"
11e5dd7070Spatrick #include "clang/Basic/DiagnosticOptions.h"
12e5dd7070Spatrick #include "clang/Basic/LLVM.h"
13e5dd7070Spatrick #include "clang/Basic/SourceLocation.h"
14e5dd7070Spatrick #include "clang/Basic/SourceManager.h"
15e5dd7070Spatrick #include "clang/Edit/Commit.h"
16e5dd7070Spatrick #include "clang/Edit/EditedSource.h"
17e5dd7070Spatrick #include "clang/Edit/EditsReceiver.h"
18e5dd7070Spatrick #include "clang/Lex/Lexer.h"
19e5dd7070Spatrick #include "llvm/ADT/ArrayRef.h"
20e5dd7070Spatrick #include "llvm/ADT/DenseMap.h"
21e5dd7070Spatrick #include "llvm/ADT/SmallString.h"
22e5dd7070Spatrick #include "llvm/ADT/SmallVector.h"
23e5dd7070Spatrick #include "llvm/ADT/StringRef.h"
24e5dd7070Spatrick #include "llvm/Support/raw_ostream.h"
25e5dd7070Spatrick #include <algorithm>
26e5dd7070Spatrick #include <cassert>
27e5dd7070Spatrick #include <iterator>
28e5dd7070Spatrick #include <utility>
29e5dd7070Spatrick 
30e5dd7070Spatrick using namespace clang;
31e5dd7070Spatrick 
DiagnosticRenderer(const LangOptions & LangOpts,DiagnosticOptions * DiagOpts)32e5dd7070Spatrick DiagnosticRenderer::DiagnosticRenderer(const LangOptions &LangOpts,
33e5dd7070Spatrick                                        DiagnosticOptions *DiagOpts)
34e5dd7070Spatrick     : LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {}
35e5dd7070Spatrick 
36e5dd7070Spatrick DiagnosticRenderer::~DiagnosticRenderer() = default;
37e5dd7070Spatrick 
38e5dd7070Spatrick namespace {
39e5dd7070Spatrick 
40e5dd7070Spatrick class FixitReceiver : public edit::EditsReceiver {
41e5dd7070Spatrick   SmallVectorImpl<FixItHint> &MergedFixits;
42e5dd7070Spatrick 
43e5dd7070Spatrick public:
FixitReceiver(SmallVectorImpl<FixItHint> & MergedFixits)44e5dd7070Spatrick   FixitReceiver(SmallVectorImpl<FixItHint> &MergedFixits)
45e5dd7070Spatrick       : MergedFixits(MergedFixits) {}
46e5dd7070Spatrick 
insert(SourceLocation loc,StringRef text)47e5dd7070Spatrick   void insert(SourceLocation loc, StringRef text) override {
48e5dd7070Spatrick     MergedFixits.push_back(FixItHint::CreateInsertion(loc, text));
49e5dd7070Spatrick   }
50e5dd7070Spatrick 
replace(CharSourceRange range,StringRef text)51e5dd7070Spatrick   void replace(CharSourceRange range, StringRef text) override {
52e5dd7070Spatrick     MergedFixits.push_back(FixItHint::CreateReplacement(range, text));
53e5dd7070Spatrick   }
54e5dd7070Spatrick };
55e5dd7070Spatrick 
56e5dd7070Spatrick } // namespace
57e5dd7070Spatrick 
mergeFixits(ArrayRef<FixItHint> FixItHints,const SourceManager & SM,const LangOptions & LangOpts,SmallVectorImpl<FixItHint> & MergedFixits)58e5dd7070Spatrick static void mergeFixits(ArrayRef<FixItHint> FixItHints,
59e5dd7070Spatrick                         const SourceManager &SM, const LangOptions &LangOpts,
60e5dd7070Spatrick                         SmallVectorImpl<FixItHint> &MergedFixits) {
61e5dd7070Spatrick   edit::Commit commit(SM, LangOpts);
62e5dd7070Spatrick   for (const auto &Hint : FixItHints)
63e5dd7070Spatrick     if (Hint.CodeToInsert.empty()) {
64e5dd7070Spatrick       if (Hint.InsertFromRange.isValid())
65e5dd7070Spatrick         commit.insertFromRange(Hint.RemoveRange.getBegin(),
66e5dd7070Spatrick                            Hint.InsertFromRange, /*afterToken=*/false,
67e5dd7070Spatrick                            Hint.BeforePreviousInsertions);
68e5dd7070Spatrick       else
69e5dd7070Spatrick         commit.remove(Hint.RemoveRange);
70e5dd7070Spatrick     } else {
71e5dd7070Spatrick       if (Hint.RemoveRange.isTokenRange() ||
72e5dd7070Spatrick           Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd())
73e5dd7070Spatrick         commit.replace(Hint.RemoveRange, Hint.CodeToInsert);
74e5dd7070Spatrick       else
75e5dd7070Spatrick         commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert,
76e5dd7070Spatrick                     /*afterToken=*/false, Hint.BeforePreviousInsertions);
77e5dd7070Spatrick     }
78e5dd7070Spatrick 
79e5dd7070Spatrick   edit::EditedSource Editor(SM, LangOpts);
80e5dd7070Spatrick   if (Editor.commit(commit)) {
81e5dd7070Spatrick     FixitReceiver Rec(MergedFixits);
82e5dd7070Spatrick     Editor.applyRewrites(Rec);
83e5dd7070Spatrick   }
84e5dd7070Spatrick }
85e5dd7070Spatrick 
emitDiagnostic(FullSourceLoc Loc,DiagnosticsEngine::Level Level,StringRef Message,ArrayRef<CharSourceRange> Ranges,ArrayRef<FixItHint> FixItHints,DiagOrStoredDiag D)86e5dd7070Spatrick void DiagnosticRenderer::emitDiagnostic(FullSourceLoc Loc,
87e5dd7070Spatrick                                         DiagnosticsEngine::Level Level,
88e5dd7070Spatrick                                         StringRef Message,
89e5dd7070Spatrick                                         ArrayRef<CharSourceRange> Ranges,
90e5dd7070Spatrick                                         ArrayRef<FixItHint> FixItHints,
91e5dd7070Spatrick                                         DiagOrStoredDiag D) {
92e5dd7070Spatrick   assert(Loc.hasManager() || Loc.isInvalid());
93e5dd7070Spatrick 
94e5dd7070Spatrick   beginDiagnostic(D, Level);
95e5dd7070Spatrick 
96e5dd7070Spatrick   if (!Loc.isValid())
97e5dd7070Spatrick     // If we have no source location, just emit the diagnostic message.
98e5dd7070Spatrick     emitDiagnosticMessage(Loc, PresumedLoc(), Level, Message, Ranges, D);
99e5dd7070Spatrick   else {
100e5dd7070Spatrick     // Get the ranges into a local array we can hack on.
101e5dd7070Spatrick     SmallVector<CharSourceRange, 20> MutableRanges(Ranges.begin(),
102e5dd7070Spatrick                                                    Ranges.end());
103e5dd7070Spatrick 
104e5dd7070Spatrick     SmallVector<FixItHint, 8> MergedFixits;
105e5dd7070Spatrick     if (!FixItHints.empty()) {
106e5dd7070Spatrick       mergeFixits(FixItHints, Loc.getManager(), LangOpts, MergedFixits);
107e5dd7070Spatrick       FixItHints = MergedFixits;
108e5dd7070Spatrick     }
109e5dd7070Spatrick 
110e5dd7070Spatrick     for (const auto &Hint : FixItHints)
111e5dd7070Spatrick       if (Hint.RemoveRange.isValid())
112e5dd7070Spatrick         MutableRanges.push_back(Hint.RemoveRange);
113e5dd7070Spatrick 
114e5dd7070Spatrick     FullSourceLoc UnexpandedLoc = Loc;
115e5dd7070Spatrick 
116e5dd7070Spatrick     // Find the ultimate expansion location for the diagnostic.
117e5dd7070Spatrick     Loc = Loc.getFileLoc();
118e5dd7070Spatrick 
119e5dd7070Spatrick     PresumedLoc PLoc = Loc.getPresumedLoc(DiagOpts->ShowPresumedLoc);
120e5dd7070Spatrick 
121e5dd7070Spatrick     // First, if this diagnostic is not in the main file, print out the
122e5dd7070Spatrick     // "included from" lines.
123e5dd7070Spatrick     emitIncludeStack(Loc, PLoc, Level);
124e5dd7070Spatrick 
125e5dd7070Spatrick     // Next, emit the actual diagnostic message and caret.
126e5dd7070Spatrick     emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, D);
127e5dd7070Spatrick     emitCaret(Loc, Level, MutableRanges, FixItHints);
128e5dd7070Spatrick 
129e5dd7070Spatrick     // If this location is within a macro, walk from UnexpandedLoc up to Loc
130e5dd7070Spatrick     // and produce a macro backtrace.
131e5dd7070Spatrick     if (UnexpandedLoc.isValid() && UnexpandedLoc.isMacroID()) {
132e5dd7070Spatrick       emitMacroExpansions(UnexpandedLoc, Level, MutableRanges, FixItHints);
133e5dd7070Spatrick     }
134e5dd7070Spatrick   }
135e5dd7070Spatrick 
136e5dd7070Spatrick   LastLoc = Loc;
137e5dd7070Spatrick   LastLevel = Level;
138e5dd7070Spatrick 
139e5dd7070Spatrick   endDiagnostic(D, Level);
140e5dd7070Spatrick }
141e5dd7070Spatrick 
emitStoredDiagnostic(StoredDiagnostic & Diag)142e5dd7070Spatrick void DiagnosticRenderer::emitStoredDiagnostic(StoredDiagnostic &Diag) {
143e5dd7070Spatrick   emitDiagnostic(Diag.getLocation(), Diag.getLevel(), Diag.getMessage(),
144e5dd7070Spatrick                  Diag.getRanges(), Diag.getFixIts(),
145e5dd7070Spatrick                  &Diag);
146e5dd7070Spatrick }
147e5dd7070Spatrick 
emitBasicNote(StringRef Message)148e5dd7070Spatrick void DiagnosticRenderer::emitBasicNote(StringRef Message) {
149e5dd7070Spatrick   emitDiagnosticMessage(FullSourceLoc(), PresumedLoc(), DiagnosticsEngine::Note,
150*12c85518Srobert                         Message, std::nullopt, DiagOrStoredDiag());
151e5dd7070Spatrick }
152e5dd7070Spatrick 
153e5dd7070Spatrick /// Prints an include stack when appropriate for a particular
154e5dd7070Spatrick /// diagnostic level and location.
155e5dd7070Spatrick ///
156e5dd7070Spatrick /// This routine handles all the logic of suppressing particular include
157e5dd7070Spatrick /// stacks (such as those for notes) and duplicate include stacks when
158e5dd7070Spatrick /// repeated warnings occur within the same file. It also handles the logic
159e5dd7070Spatrick /// of customizing the formatting and display of the include stack.
160e5dd7070Spatrick ///
161e5dd7070Spatrick /// \param Loc   The diagnostic location.
162e5dd7070Spatrick /// \param PLoc  The presumed location of the diagnostic location.
163e5dd7070Spatrick /// \param Level The diagnostic level of the message this stack pertains to.
emitIncludeStack(FullSourceLoc Loc,PresumedLoc PLoc,DiagnosticsEngine::Level Level)164e5dd7070Spatrick void DiagnosticRenderer::emitIncludeStack(FullSourceLoc Loc, PresumedLoc PLoc,
165e5dd7070Spatrick                                           DiagnosticsEngine::Level Level) {
166e5dd7070Spatrick   FullSourceLoc IncludeLoc =
167e5dd7070Spatrick       PLoc.isInvalid() ? FullSourceLoc()
168e5dd7070Spatrick                        : FullSourceLoc(PLoc.getIncludeLoc(), Loc.getManager());
169e5dd7070Spatrick 
170e5dd7070Spatrick   // Skip redundant include stacks altogether.
171e5dd7070Spatrick   if (LastIncludeLoc == IncludeLoc)
172e5dd7070Spatrick     return;
173e5dd7070Spatrick 
174e5dd7070Spatrick   LastIncludeLoc = IncludeLoc;
175e5dd7070Spatrick 
176e5dd7070Spatrick   if (!DiagOpts->ShowNoteIncludeStack && Level == DiagnosticsEngine::Note)
177e5dd7070Spatrick     return;
178e5dd7070Spatrick 
179e5dd7070Spatrick   if (IncludeLoc.isValid())
180e5dd7070Spatrick     emitIncludeStackRecursively(IncludeLoc);
181e5dd7070Spatrick   else {
182e5dd7070Spatrick     emitModuleBuildStack(Loc.getManager());
183e5dd7070Spatrick     emitImportStack(Loc);
184e5dd7070Spatrick   }
185e5dd7070Spatrick }
186e5dd7070Spatrick 
187e5dd7070Spatrick /// Helper to recursively walk up the include stack and print each layer
188e5dd7070Spatrick /// on the way back down.
emitIncludeStackRecursively(FullSourceLoc Loc)189e5dd7070Spatrick void DiagnosticRenderer::emitIncludeStackRecursively(FullSourceLoc Loc) {
190e5dd7070Spatrick   if (Loc.isInvalid()) {
191e5dd7070Spatrick     emitModuleBuildStack(Loc.getManager());
192e5dd7070Spatrick     return;
193e5dd7070Spatrick   }
194e5dd7070Spatrick 
195e5dd7070Spatrick   PresumedLoc PLoc = Loc.getPresumedLoc(DiagOpts->ShowPresumedLoc);
196e5dd7070Spatrick   if (PLoc.isInvalid())
197e5dd7070Spatrick     return;
198e5dd7070Spatrick 
199e5dd7070Spatrick   // If this source location was imported from a module, print the module
200e5dd7070Spatrick   // import stack rather than the
201e5dd7070Spatrick   // FIXME: We want submodule granularity here.
202e5dd7070Spatrick   std::pair<FullSourceLoc, StringRef> Imported = Loc.getModuleImportLoc();
203e5dd7070Spatrick   if (!Imported.second.empty()) {
204e5dd7070Spatrick     // This location was imported by a module. Emit the module import stack.
205e5dd7070Spatrick     emitImportStackRecursively(Imported.first, Imported.second);
206e5dd7070Spatrick     return;
207e5dd7070Spatrick   }
208e5dd7070Spatrick 
209e5dd7070Spatrick   // Emit the other include frames first.
210e5dd7070Spatrick   emitIncludeStackRecursively(
211e5dd7070Spatrick       FullSourceLoc(PLoc.getIncludeLoc(), Loc.getManager()));
212e5dd7070Spatrick 
213e5dd7070Spatrick   // Emit the inclusion text/note.
214e5dd7070Spatrick   emitIncludeLocation(Loc, PLoc);
215e5dd7070Spatrick }
216e5dd7070Spatrick 
217e5dd7070Spatrick /// Emit the module import stack associated with the current location.
emitImportStack(FullSourceLoc Loc)218e5dd7070Spatrick void DiagnosticRenderer::emitImportStack(FullSourceLoc Loc) {
219e5dd7070Spatrick   if (Loc.isInvalid()) {
220e5dd7070Spatrick     emitModuleBuildStack(Loc.getManager());
221e5dd7070Spatrick     return;
222e5dd7070Spatrick   }
223e5dd7070Spatrick 
224e5dd7070Spatrick   std::pair<FullSourceLoc, StringRef> NextImportLoc = Loc.getModuleImportLoc();
225e5dd7070Spatrick   emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second);
226e5dd7070Spatrick }
227e5dd7070Spatrick 
228e5dd7070Spatrick /// Helper to recursively walk up the import stack and print each layer
229e5dd7070Spatrick /// on the way back down.
emitImportStackRecursively(FullSourceLoc Loc,StringRef ModuleName)230e5dd7070Spatrick void DiagnosticRenderer::emitImportStackRecursively(FullSourceLoc Loc,
231e5dd7070Spatrick                                                     StringRef ModuleName) {
232e5dd7070Spatrick   if (ModuleName.empty()) {
233e5dd7070Spatrick     return;
234e5dd7070Spatrick   }
235e5dd7070Spatrick 
236e5dd7070Spatrick   PresumedLoc PLoc = Loc.getPresumedLoc(DiagOpts->ShowPresumedLoc);
237e5dd7070Spatrick 
238e5dd7070Spatrick   // Emit the other import frames first.
239e5dd7070Spatrick   std::pair<FullSourceLoc, StringRef> NextImportLoc = Loc.getModuleImportLoc();
240e5dd7070Spatrick   emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second);
241e5dd7070Spatrick 
242e5dd7070Spatrick   // Emit the inclusion text/note.
243e5dd7070Spatrick   emitImportLocation(Loc, PLoc, ModuleName);
244e5dd7070Spatrick }
245e5dd7070Spatrick 
246e5dd7070Spatrick /// Emit the module build stack, for cases where a module is (re-)built
247e5dd7070Spatrick /// on demand.
emitModuleBuildStack(const SourceManager & SM)248e5dd7070Spatrick void DiagnosticRenderer::emitModuleBuildStack(const SourceManager &SM) {
249e5dd7070Spatrick   ModuleBuildStack Stack = SM.getModuleBuildStack();
250e5dd7070Spatrick   for (const auto &I : Stack) {
251e5dd7070Spatrick     emitBuildingModuleLocation(I.second, I.second.getPresumedLoc(
252e5dd7070Spatrick                                               DiagOpts->ShowPresumedLoc),
253e5dd7070Spatrick                                I.first);
254e5dd7070Spatrick   }
255e5dd7070Spatrick }
256e5dd7070Spatrick 
257e5dd7070Spatrick /// A recursive function to trace all possible backtrace locations
258e5dd7070Spatrick /// to match the \p CaretLocFileID.
259e5dd7070Spatrick static SourceLocation
retrieveMacroLocation(SourceLocation Loc,FileID MacroFileID,FileID CaretFileID,const SmallVectorImpl<FileID> & CommonArgExpansions,bool IsBegin,const SourceManager * SM,bool & IsTokenRange)260e5dd7070Spatrick retrieveMacroLocation(SourceLocation Loc, FileID MacroFileID,
261e5dd7070Spatrick                       FileID CaretFileID,
262e5dd7070Spatrick                       const SmallVectorImpl<FileID> &CommonArgExpansions,
263e5dd7070Spatrick                       bool IsBegin, const SourceManager *SM,
264e5dd7070Spatrick                       bool &IsTokenRange) {
265e5dd7070Spatrick   assert(SM->getFileID(Loc) == MacroFileID);
266e5dd7070Spatrick   if (MacroFileID == CaretFileID)
267e5dd7070Spatrick     return Loc;
268e5dd7070Spatrick   if (!Loc.isMacroID())
269e5dd7070Spatrick     return {};
270e5dd7070Spatrick 
271e5dd7070Spatrick   CharSourceRange MacroRange, MacroArgRange;
272e5dd7070Spatrick 
273e5dd7070Spatrick   if (SM->isMacroArgExpansion(Loc)) {
274e5dd7070Spatrick     // Only look at the immediate spelling location of this macro argument if
275e5dd7070Spatrick     // the other location in the source range is also present in that expansion.
276e5dd7070Spatrick     if (std::binary_search(CommonArgExpansions.begin(),
277e5dd7070Spatrick                            CommonArgExpansions.end(), MacroFileID))
278e5dd7070Spatrick       MacroRange =
279e5dd7070Spatrick           CharSourceRange(SM->getImmediateSpellingLoc(Loc), IsTokenRange);
280e5dd7070Spatrick     MacroArgRange = SM->getImmediateExpansionRange(Loc);
281e5dd7070Spatrick   } else {
282e5dd7070Spatrick     MacroRange = SM->getImmediateExpansionRange(Loc);
283e5dd7070Spatrick     MacroArgRange =
284e5dd7070Spatrick         CharSourceRange(SM->getImmediateSpellingLoc(Loc), IsTokenRange);
285e5dd7070Spatrick   }
286e5dd7070Spatrick 
287e5dd7070Spatrick   SourceLocation MacroLocation =
288e5dd7070Spatrick       IsBegin ? MacroRange.getBegin() : MacroRange.getEnd();
289e5dd7070Spatrick   if (MacroLocation.isValid()) {
290e5dd7070Spatrick     MacroFileID = SM->getFileID(MacroLocation);
291e5dd7070Spatrick     bool TokenRange = IsBegin ? IsTokenRange : MacroRange.isTokenRange();
292e5dd7070Spatrick     MacroLocation =
293e5dd7070Spatrick         retrieveMacroLocation(MacroLocation, MacroFileID, CaretFileID,
294e5dd7070Spatrick                               CommonArgExpansions, IsBegin, SM, TokenRange);
295e5dd7070Spatrick     if (MacroLocation.isValid()) {
296e5dd7070Spatrick       IsTokenRange = TokenRange;
297e5dd7070Spatrick       return MacroLocation;
298e5dd7070Spatrick     }
299e5dd7070Spatrick   }
300e5dd7070Spatrick 
301e5dd7070Spatrick   // If we moved the end of the range to an expansion location, we now have
302e5dd7070Spatrick   // a range of the same kind as the expansion range.
303e5dd7070Spatrick   if (!IsBegin)
304e5dd7070Spatrick     IsTokenRange = MacroArgRange.isTokenRange();
305e5dd7070Spatrick 
306e5dd7070Spatrick   SourceLocation MacroArgLocation =
307e5dd7070Spatrick       IsBegin ? MacroArgRange.getBegin() : MacroArgRange.getEnd();
308e5dd7070Spatrick   MacroFileID = SM->getFileID(MacroArgLocation);
309e5dd7070Spatrick   return retrieveMacroLocation(MacroArgLocation, MacroFileID, CaretFileID,
310e5dd7070Spatrick                                CommonArgExpansions, IsBegin, SM, IsTokenRange);
311e5dd7070Spatrick }
312e5dd7070Spatrick 
313e5dd7070Spatrick /// Walk up the chain of macro expansions and collect the FileIDs identifying the
314e5dd7070Spatrick /// expansions.
getMacroArgExpansionFileIDs(SourceLocation Loc,SmallVectorImpl<FileID> & IDs,bool IsBegin,const SourceManager * SM)315e5dd7070Spatrick static void getMacroArgExpansionFileIDs(SourceLocation Loc,
316e5dd7070Spatrick                                         SmallVectorImpl<FileID> &IDs,
317e5dd7070Spatrick                                         bool IsBegin, const SourceManager *SM) {
318e5dd7070Spatrick   while (Loc.isMacroID()) {
319e5dd7070Spatrick     if (SM->isMacroArgExpansion(Loc)) {
320e5dd7070Spatrick       IDs.push_back(SM->getFileID(Loc));
321e5dd7070Spatrick       Loc = SM->getImmediateSpellingLoc(Loc);
322e5dd7070Spatrick     } else {
323e5dd7070Spatrick       auto ExpRange = SM->getImmediateExpansionRange(Loc);
324e5dd7070Spatrick       Loc = IsBegin ? ExpRange.getBegin() : ExpRange.getEnd();
325e5dd7070Spatrick     }
326e5dd7070Spatrick   }
327e5dd7070Spatrick }
328e5dd7070Spatrick 
329e5dd7070Spatrick /// Collect the expansions of the begin and end locations and compute the set
330e5dd7070Spatrick /// intersection. Produces a sorted vector of FileIDs in CommonArgExpansions.
computeCommonMacroArgExpansionFileIDs(SourceLocation Begin,SourceLocation End,const SourceManager * SM,SmallVectorImpl<FileID> & CommonArgExpansions)331e5dd7070Spatrick static void computeCommonMacroArgExpansionFileIDs(
332e5dd7070Spatrick     SourceLocation Begin, SourceLocation End, const SourceManager *SM,
333e5dd7070Spatrick     SmallVectorImpl<FileID> &CommonArgExpansions) {
334e5dd7070Spatrick   SmallVector<FileID, 4> BeginArgExpansions;
335e5dd7070Spatrick   SmallVector<FileID, 4> EndArgExpansions;
336e5dd7070Spatrick   getMacroArgExpansionFileIDs(Begin, BeginArgExpansions, /*IsBegin=*/true, SM);
337e5dd7070Spatrick   getMacroArgExpansionFileIDs(End, EndArgExpansions, /*IsBegin=*/false, SM);
338e5dd7070Spatrick   llvm::sort(BeginArgExpansions);
339e5dd7070Spatrick   llvm::sort(EndArgExpansions);
340e5dd7070Spatrick   std::set_intersection(BeginArgExpansions.begin(), BeginArgExpansions.end(),
341e5dd7070Spatrick                         EndArgExpansions.begin(), EndArgExpansions.end(),
342e5dd7070Spatrick                         std::back_inserter(CommonArgExpansions));
343e5dd7070Spatrick }
344e5dd7070Spatrick 
345e5dd7070Spatrick // Helper function to fix up source ranges.  It takes in an array of ranges,
346e5dd7070Spatrick // and outputs an array of ranges where we want to draw the range highlighting
347e5dd7070Spatrick // around the location specified by CaretLoc.
348e5dd7070Spatrick //
349e5dd7070Spatrick // To find locations which correspond to the caret, we crawl the macro caller
350e5dd7070Spatrick // chain for the beginning and end of each range.  If the caret location
351e5dd7070Spatrick // is in a macro expansion, we search each chain for a location
352e5dd7070Spatrick // in the same expansion as the caret; otherwise, we crawl to the top of
353e5dd7070Spatrick // each chain. Two locations are part of the same macro expansion
354e5dd7070Spatrick // iff the FileID is the same.
355e5dd7070Spatrick static void
mapDiagnosticRanges(FullSourceLoc CaretLoc,ArrayRef<CharSourceRange> Ranges,SmallVectorImpl<CharSourceRange> & SpellingRanges)356e5dd7070Spatrick mapDiagnosticRanges(FullSourceLoc CaretLoc, ArrayRef<CharSourceRange> Ranges,
357e5dd7070Spatrick                     SmallVectorImpl<CharSourceRange> &SpellingRanges) {
358e5dd7070Spatrick   FileID CaretLocFileID = CaretLoc.getFileID();
359e5dd7070Spatrick 
360e5dd7070Spatrick   const SourceManager *SM = &CaretLoc.getManager();
361e5dd7070Spatrick 
362e5dd7070Spatrick   for (const auto &Range : Ranges) {
363e5dd7070Spatrick     if (Range.isInvalid())
364e5dd7070Spatrick       continue;
365e5dd7070Spatrick 
366e5dd7070Spatrick     SourceLocation Begin = Range.getBegin(), End = Range.getEnd();
367e5dd7070Spatrick     bool IsTokenRange = Range.isTokenRange();
368e5dd7070Spatrick 
369e5dd7070Spatrick     FileID BeginFileID = SM->getFileID(Begin);
370e5dd7070Spatrick     FileID EndFileID = SM->getFileID(End);
371e5dd7070Spatrick 
372e5dd7070Spatrick     // Find the common parent for the beginning and end of the range.
373e5dd7070Spatrick 
374e5dd7070Spatrick     // First, crawl the expansion chain for the beginning of the range.
375e5dd7070Spatrick     llvm::SmallDenseMap<FileID, SourceLocation> BeginLocsMap;
376e5dd7070Spatrick     while (Begin.isMacroID() && BeginFileID != EndFileID) {
377e5dd7070Spatrick       BeginLocsMap[BeginFileID] = Begin;
378e5dd7070Spatrick       Begin = SM->getImmediateExpansionRange(Begin).getBegin();
379e5dd7070Spatrick       BeginFileID = SM->getFileID(Begin);
380e5dd7070Spatrick     }
381e5dd7070Spatrick 
382e5dd7070Spatrick     // Then, crawl the expansion chain for the end of the range.
383e5dd7070Spatrick     if (BeginFileID != EndFileID) {
384e5dd7070Spatrick       while (End.isMacroID() && !BeginLocsMap.count(EndFileID)) {
385e5dd7070Spatrick         auto Exp = SM->getImmediateExpansionRange(End);
386e5dd7070Spatrick         IsTokenRange = Exp.isTokenRange();
387e5dd7070Spatrick         End = Exp.getEnd();
388e5dd7070Spatrick         EndFileID = SM->getFileID(End);
389e5dd7070Spatrick       }
390e5dd7070Spatrick       if (End.isMacroID()) {
391e5dd7070Spatrick         Begin = BeginLocsMap[EndFileID];
392e5dd7070Spatrick         BeginFileID = EndFileID;
393e5dd7070Spatrick       }
394e5dd7070Spatrick     }
395e5dd7070Spatrick 
396a9ac8606Spatrick     // There is a chance that begin or end is invalid here, for example if
397a9ac8606Spatrick     // specific compile error is reported.
398a9ac8606Spatrick     // It is possible that the FileID's do not match, if one comes from an
399a9ac8606Spatrick     // included file. In this case we can not produce a meaningful source range.
400a9ac8606Spatrick     if (Begin.isInvalid() || End.isInvalid() || BeginFileID != EndFileID)
401a9ac8606Spatrick       continue;
402a9ac8606Spatrick 
403e5dd7070Spatrick     // Do the backtracking.
404e5dd7070Spatrick     SmallVector<FileID, 4> CommonArgExpansions;
405e5dd7070Spatrick     computeCommonMacroArgExpansionFileIDs(Begin, End, SM, CommonArgExpansions);
406e5dd7070Spatrick     Begin = retrieveMacroLocation(Begin, BeginFileID, CaretLocFileID,
407e5dd7070Spatrick                                   CommonArgExpansions, /*IsBegin=*/true, SM,
408e5dd7070Spatrick                                   IsTokenRange);
409e5dd7070Spatrick     End = retrieveMacroLocation(End, BeginFileID, CaretLocFileID,
410e5dd7070Spatrick                                 CommonArgExpansions, /*IsBegin=*/false, SM,
411e5dd7070Spatrick                                 IsTokenRange);
412e5dd7070Spatrick     if (Begin.isInvalid() || End.isInvalid()) continue;
413e5dd7070Spatrick 
414e5dd7070Spatrick     // Return the spelling location of the beginning and end of the range.
415e5dd7070Spatrick     Begin = SM->getSpellingLoc(Begin);
416e5dd7070Spatrick     End = SM->getSpellingLoc(End);
417e5dd7070Spatrick 
418e5dd7070Spatrick     SpellingRanges.push_back(CharSourceRange(SourceRange(Begin, End),
419e5dd7070Spatrick                                              IsTokenRange));
420e5dd7070Spatrick   }
421e5dd7070Spatrick }
422e5dd7070Spatrick 
emitCaret(FullSourceLoc Loc,DiagnosticsEngine::Level Level,ArrayRef<CharSourceRange> Ranges,ArrayRef<FixItHint> Hints)423e5dd7070Spatrick void DiagnosticRenderer::emitCaret(FullSourceLoc Loc,
424e5dd7070Spatrick                                    DiagnosticsEngine::Level Level,
425e5dd7070Spatrick                                    ArrayRef<CharSourceRange> Ranges,
426e5dd7070Spatrick                                    ArrayRef<FixItHint> Hints) {
427e5dd7070Spatrick   SmallVector<CharSourceRange, 4> SpellingRanges;
428e5dd7070Spatrick   mapDiagnosticRanges(Loc, Ranges, SpellingRanges);
429e5dd7070Spatrick   emitCodeContext(Loc, Level, SpellingRanges, Hints);
430e5dd7070Spatrick }
431e5dd7070Spatrick 
432e5dd7070Spatrick /// A helper function for emitMacroExpansion to print the
433e5dd7070Spatrick /// macro expansion message
emitSingleMacroExpansion(FullSourceLoc Loc,DiagnosticsEngine::Level Level,ArrayRef<CharSourceRange> Ranges)434e5dd7070Spatrick void DiagnosticRenderer::emitSingleMacroExpansion(
435e5dd7070Spatrick     FullSourceLoc Loc, DiagnosticsEngine::Level Level,
436e5dd7070Spatrick     ArrayRef<CharSourceRange> Ranges) {
437e5dd7070Spatrick   // Find the spelling location for the macro definition. We must use the
438e5dd7070Spatrick   // spelling location here to avoid emitting a macro backtrace for the note.
439e5dd7070Spatrick   FullSourceLoc SpellingLoc = Loc.getSpellingLoc();
440e5dd7070Spatrick 
441e5dd7070Spatrick   // Map the ranges into the FileID of the diagnostic location.
442e5dd7070Spatrick   SmallVector<CharSourceRange, 4> SpellingRanges;
443e5dd7070Spatrick   mapDiagnosticRanges(Loc, Ranges, SpellingRanges);
444e5dd7070Spatrick 
445e5dd7070Spatrick   SmallString<100> MessageStorage;
446e5dd7070Spatrick   llvm::raw_svector_ostream Message(MessageStorage);
447e5dd7070Spatrick   StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics(
448e5dd7070Spatrick       Loc, Loc.getManager(), LangOpts);
449e5dd7070Spatrick   if (MacroName.empty())
450e5dd7070Spatrick     Message << "expanded from here";
451e5dd7070Spatrick   else
452e5dd7070Spatrick     Message << "expanded from macro '" << MacroName << "'";
453e5dd7070Spatrick 
454e5dd7070Spatrick   emitDiagnostic(SpellingLoc, DiagnosticsEngine::Note, Message.str(),
455*12c85518Srobert                  SpellingRanges, std::nullopt);
456e5dd7070Spatrick }
457e5dd7070Spatrick 
458e5dd7070Spatrick /// Check that the macro argument location of Loc starts with ArgumentLoc.
459e5dd7070Spatrick /// The starting location of the macro expansions is used to differeniate
460e5dd7070Spatrick /// different macro expansions.
checkLocForMacroArgExpansion(SourceLocation Loc,const SourceManager & SM,SourceLocation ArgumentLoc)461e5dd7070Spatrick static bool checkLocForMacroArgExpansion(SourceLocation Loc,
462e5dd7070Spatrick                                          const SourceManager &SM,
463e5dd7070Spatrick                                          SourceLocation ArgumentLoc) {
464e5dd7070Spatrick   SourceLocation MacroLoc;
465e5dd7070Spatrick   if (SM.isMacroArgExpansion(Loc, &MacroLoc)) {
466e5dd7070Spatrick     if (ArgumentLoc == MacroLoc) return true;
467e5dd7070Spatrick   }
468e5dd7070Spatrick 
469e5dd7070Spatrick   return false;
470e5dd7070Spatrick }
471e5dd7070Spatrick 
472e5dd7070Spatrick /// Check if all the locations in the range have the same macro argument
473e5dd7070Spatrick /// expansion, and that the expansion starts with ArgumentLoc.
checkRangeForMacroArgExpansion(CharSourceRange Range,const SourceManager & SM,SourceLocation ArgumentLoc)474e5dd7070Spatrick static bool checkRangeForMacroArgExpansion(CharSourceRange Range,
475e5dd7070Spatrick                                            const SourceManager &SM,
476e5dd7070Spatrick                                            SourceLocation ArgumentLoc) {
477e5dd7070Spatrick   SourceLocation BegLoc = Range.getBegin(), EndLoc = Range.getEnd();
478e5dd7070Spatrick   while (BegLoc != EndLoc) {
479e5dd7070Spatrick     if (!checkLocForMacroArgExpansion(BegLoc, SM, ArgumentLoc))
480e5dd7070Spatrick       return false;
481e5dd7070Spatrick     BegLoc.getLocWithOffset(1);
482e5dd7070Spatrick   }
483e5dd7070Spatrick 
484e5dd7070Spatrick   return checkLocForMacroArgExpansion(BegLoc, SM, ArgumentLoc);
485e5dd7070Spatrick }
486e5dd7070Spatrick 
487e5dd7070Spatrick /// A helper function to check if the current ranges are all inside the same
488e5dd7070Spatrick /// macro argument expansion as Loc.
checkRangesForMacroArgExpansion(FullSourceLoc Loc,ArrayRef<CharSourceRange> Ranges)489e5dd7070Spatrick static bool checkRangesForMacroArgExpansion(FullSourceLoc Loc,
490e5dd7070Spatrick                                             ArrayRef<CharSourceRange> Ranges) {
491e5dd7070Spatrick   assert(Loc.isMacroID() && "Must be a macro expansion!");
492e5dd7070Spatrick 
493e5dd7070Spatrick   SmallVector<CharSourceRange, 4> SpellingRanges;
494e5dd7070Spatrick   mapDiagnosticRanges(Loc, Ranges, SpellingRanges);
495e5dd7070Spatrick 
496e5dd7070Spatrick   /// Count all valid ranges.
497e5dd7070Spatrick   unsigned ValidCount = 0;
498e5dd7070Spatrick   for (const auto &Range : Ranges)
499e5dd7070Spatrick     if (Range.isValid())
500e5dd7070Spatrick       ValidCount++;
501e5dd7070Spatrick 
502e5dd7070Spatrick   if (ValidCount > SpellingRanges.size())
503e5dd7070Spatrick     return false;
504e5dd7070Spatrick 
505e5dd7070Spatrick   /// To store the source location of the argument location.
506e5dd7070Spatrick   FullSourceLoc ArgumentLoc;
507e5dd7070Spatrick 
508e5dd7070Spatrick   /// Set the ArgumentLoc to the beginning location of the expansion of Loc
509e5dd7070Spatrick   /// so to check if the ranges expands to the same beginning location.
510e5dd7070Spatrick   if (!Loc.isMacroArgExpansion(&ArgumentLoc))
511e5dd7070Spatrick     return false;
512e5dd7070Spatrick 
513e5dd7070Spatrick   for (const auto &Range : SpellingRanges)
514e5dd7070Spatrick     if (!checkRangeForMacroArgExpansion(Range, Loc.getManager(), ArgumentLoc))
515e5dd7070Spatrick       return false;
516e5dd7070Spatrick 
517e5dd7070Spatrick   return true;
518e5dd7070Spatrick }
519e5dd7070Spatrick 
520e5dd7070Spatrick /// Recursively emit notes for each macro expansion and caret
521e5dd7070Spatrick /// diagnostics where appropriate.
522e5dd7070Spatrick ///
523e5dd7070Spatrick /// Walks up the macro expansion stack printing expansion notes, the code
524e5dd7070Spatrick /// snippet, caret, underlines and FixItHint display as appropriate at each
525e5dd7070Spatrick /// level.
526e5dd7070Spatrick ///
527e5dd7070Spatrick /// \param Loc The location for this caret.
528e5dd7070Spatrick /// \param Level The diagnostic level currently being emitted.
529e5dd7070Spatrick /// \param Ranges The underlined ranges for this code snippet.
530e5dd7070Spatrick /// \param Hints The FixIt hints active for this diagnostic.
emitMacroExpansions(FullSourceLoc Loc,DiagnosticsEngine::Level Level,ArrayRef<CharSourceRange> Ranges,ArrayRef<FixItHint> Hints)531e5dd7070Spatrick void DiagnosticRenderer::emitMacroExpansions(FullSourceLoc Loc,
532e5dd7070Spatrick                                              DiagnosticsEngine::Level Level,
533e5dd7070Spatrick                                              ArrayRef<CharSourceRange> Ranges,
534e5dd7070Spatrick                                              ArrayRef<FixItHint> Hints) {
535e5dd7070Spatrick   assert(Loc.isValid() && "must have a valid source location here");
536e5dd7070Spatrick   const SourceManager &SM = Loc.getManager();
537e5dd7070Spatrick   SourceLocation L = Loc;
538e5dd7070Spatrick 
539e5dd7070Spatrick   // Produce a stack of macro backtraces.
540e5dd7070Spatrick   SmallVector<SourceLocation, 8> LocationStack;
541e5dd7070Spatrick   unsigned IgnoredEnd = 0;
542e5dd7070Spatrick   while (L.isMacroID()) {
543e5dd7070Spatrick     // If this is the expansion of a macro argument, point the caret at the
544e5dd7070Spatrick     // use of the argument in the definition of the macro, not the expansion.
545e5dd7070Spatrick     if (SM.isMacroArgExpansion(L))
546e5dd7070Spatrick       LocationStack.push_back(SM.getImmediateExpansionRange(L).getBegin());
547e5dd7070Spatrick     else
548e5dd7070Spatrick       LocationStack.push_back(L);
549e5dd7070Spatrick 
550e5dd7070Spatrick     if (checkRangesForMacroArgExpansion(FullSourceLoc(L, SM), Ranges))
551e5dd7070Spatrick       IgnoredEnd = LocationStack.size();
552e5dd7070Spatrick 
553e5dd7070Spatrick     L = SM.getImmediateMacroCallerLoc(L);
554e5dd7070Spatrick 
555e5dd7070Spatrick     // Once the location no longer points into a macro, try stepping through
556e5dd7070Spatrick     // the last found location.  This sometimes produces additional useful
557e5dd7070Spatrick     // backtraces.
558e5dd7070Spatrick     if (L.isFileID())
559e5dd7070Spatrick       L = SM.getImmediateMacroCallerLoc(LocationStack.back());
560e5dd7070Spatrick     assert(L.isValid() && "must have a valid source location here");
561e5dd7070Spatrick   }
562e5dd7070Spatrick 
563e5dd7070Spatrick   LocationStack.erase(LocationStack.begin(),
564e5dd7070Spatrick                       LocationStack.begin() + IgnoredEnd);
565e5dd7070Spatrick 
566e5dd7070Spatrick   unsigned MacroDepth = LocationStack.size();
567e5dd7070Spatrick   unsigned MacroLimit = DiagOpts->MacroBacktraceLimit;
568e5dd7070Spatrick   if (MacroDepth <= MacroLimit || MacroLimit == 0) {
569e5dd7070Spatrick     for (auto I = LocationStack.rbegin(), E = LocationStack.rend();
570e5dd7070Spatrick          I != E; ++I)
571e5dd7070Spatrick       emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges);
572e5dd7070Spatrick     return;
573e5dd7070Spatrick   }
574e5dd7070Spatrick 
575e5dd7070Spatrick   unsigned MacroStartMessages = MacroLimit / 2;
576e5dd7070Spatrick   unsigned MacroEndMessages = MacroLimit / 2 + MacroLimit % 2;
577e5dd7070Spatrick 
578e5dd7070Spatrick   for (auto I = LocationStack.rbegin(),
579e5dd7070Spatrick             E = LocationStack.rbegin() + MacroStartMessages;
580e5dd7070Spatrick        I != E; ++I)
581e5dd7070Spatrick     emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges);
582e5dd7070Spatrick 
583e5dd7070Spatrick   SmallString<200> MessageStorage;
584e5dd7070Spatrick   llvm::raw_svector_ostream Message(MessageStorage);
585e5dd7070Spatrick   Message << "(skipping " << (MacroDepth - MacroLimit)
586e5dd7070Spatrick           << " expansions in backtrace; use -fmacro-backtrace-limit=0 to "
587e5dd7070Spatrick              "see all)";
588e5dd7070Spatrick   emitBasicNote(Message.str());
589e5dd7070Spatrick 
590e5dd7070Spatrick   for (auto I = LocationStack.rend() - MacroEndMessages,
591e5dd7070Spatrick             E = LocationStack.rend();
592e5dd7070Spatrick        I != E; ++I)
593e5dd7070Spatrick     emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges);
594e5dd7070Spatrick }
595e5dd7070Spatrick 
596e5dd7070Spatrick DiagnosticNoteRenderer::~DiagnosticNoteRenderer() = default;
597e5dd7070Spatrick 
emitIncludeLocation(FullSourceLoc Loc,PresumedLoc PLoc)598e5dd7070Spatrick void DiagnosticNoteRenderer::emitIncludeLocation(FullSourceLoc Loc,
599e5dd7070Spatrick                                                  PresumedLoc PLoc) {
600e5dd7070Spatrick   // Generate a note indicating the include location.
601e5dd7070Spatrick   SmallString<200> MessageStorage;
602e5dd7070Spatrick   llvm::raw_svector_ostream Message(MessageStorage);
603e5dd7070Spatrick   Message << "in file included from " << PLoc.getFilename() << ':'
604e5dd7070Spatrick           << PLoc.getLine() << ":";
605e5dd7070Spatrick   emitNote(Loc, Message.str());
606e5dd7070Spatrick }
607e5dd7070Spatrick 
emitImportLocation(FullSourceLoc Loc,PresumedLoc PLoc,StringRef ModuleName)608e5dd7070Spatrick void DiagnosticNoteRenderer::emitImportLocation(FullSourceLoc Loc,
609e5dd7070Spatrick                                                 PresumedLoc PLoc,
610e5dd7070Spatrick                                                 StringRef ModuleName) {
611e5dd7070Spatrick   // Generate a note indicating the include location.
612e5dd7070Spatrick   SmallString<200> MessageStorage;
613e5dd7070Spatrick   llvm::raw_svector_ostream Message(MessageStorage);
614e5dd7070Spatrick   Message << "in module '" << ModuleName;
615e5dd7070Spatrick   if (PLoc.isValid())
616e5dd7070Spatrick     Message << "' imported from " << PLoc.getFilename() << ':'
617e5dd7070Spatrick             << PLoc.getLine();
618e5dd7070Spatrick   Message << ":";
619e5dd7070Spatrick   emitNote(Loc, Message.str());
620e5dd7070Spatrick }
621e5dd7070Spatrick 
emitBuildingModuleLocation(FullSourceLoc Loc,PresumedLoc PLoc,StringRef ModuleName)622e5dd7070Spatrick void DiagnosticNoteRenderer::emitBuildingModuleLocation(FullSourceLoc Loc,
623e5dd7070Spatrick                                                         PresumedLoc PLoc,
624e5dd7070Spatrick                                                         StringRef ModuleName) {
625e5dd7070Spatrick   // Generate a note indicating the include location.
626e5dd7070Spatrick   SmallString<200> MessageStorage;
627e5dd7070Spatrick   llvm::raw_svector_ostream Message(MessageStorage);
628e5dd7070Spatrick   if (PLoc.isValid())
629e5dd7070Spatrick     Message << "while building module '" << ModuleName << "' imported from "
630e5dd7070Spatrick             << PLoc.getFilename() << ':' << PLoc.getLine() << ":";
631e5dd7070Spatrick   else
632e5dd7070Spatrick     Message << "while building module '" << ModuleName << "':";
633e5dd7070Spatrick   emitNote(Loc, Message.str());
634e5dd7070Spatrick }
635