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