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