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