/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 | 81.9k | : LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {} |
36 | | |
37 | 81.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 | 2.96k | : MergedFixits(MergedFixits) {} |
47 | | |
48 | 1.65k | void insert(SourceLocation loc, StringRef text) override { |
49 | 1.65k | MergedFixits.push_back(FixItHint::CreateInsertion(loc, text)); |
50 | 1.65k | } |
51 | | |
52 | 1.98k | void replace(CharSourceRange range, StringRef text) override { |
53 | 1.98k | MergedFixits.push_back(FixItHint::CreateReplacement(range, text)); |
54 | 1.98k | } |
55 | | }; |
56 | | |
57 | | } // namespace |
58 | | |
59 | | static void mergeFixits(ArrayRef<FixItHint> FixItHints, |
60 | | const SourceManager &SM, const LangOptions &LangOpts, |
61 | 2.97k | SmallVectorImpl<FixItHint> &MergedFixits) { |
62 | 2.97k | edit::Commit commit(SM, LangOpts); |
63 | 2.97k | for (const auto &Hint : FixItHints) |
64 | 3.66k | if (Hint.CodeToInsert.empty()) { |
65 | 615 | if (Hint.InsertFromRange.isValid()) |
66 | 54 | commit.insertFromRange(Hint.RemoveRange.getBegin(), |
67 | 54 | Hint.InsertFromRange, /*afterToken=*/false, |
68 | 54 | Hint.BeforePreviousInsertions); |
69 | 561 | else |
70 | 561 | commit.remove(Hint.RemoveRange); |
71 | 3.05k | } else { |
72 | 3.05k | if (Hint.RemoveRange.isTokenRange() || |
73 | 2.02k | Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd()) |
74 | 1.43k | commit.replace(Hint.RemoveRange, Hint.CodeToInsert); |
75 | 1.61k | else |
76 | 1.61k | commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert, |
77 | 1.61k | /*afterToken=*/false, Hint.BeforePreviousInsertions); |
78 | 3.05k | } |
79 | | |
80 | 2.97k | edit::EditedSource Editor(SM, LangOpts); |
81 | 2.97k | if (Editor.commit(commit)) { |
82 | 2.96k | FixitReceiver Rec(MergedFixits); |
83 | 2.96k | Editor.applyRewrites(Rec); |
84 | 2.96k | } |
85 | 2.97k | } |
86 | | |
87 | | void DiagnosticRenderer::emitDiagnostic(FullSourceLoc Loc, |
88 | | DiagnosticsEngine::Level Level, |
89 | | StringRef Message, |
90 | | ArrayRef<CharSourceRange> Ranges, |
91 | | ArrayRef<FixItHint> FixItHints, |
92 | 27.2k | DiagOrStoredDiag D) { |
93 | 27.2k | assert(Loc.hasManager() || Loc.isInvalid()); |
94 | | |
95 | 27.2k | beginDiagnostic(D, Level); |
96 | | |
97 | 27.2k | if (!Loc.isValid()) |
98 | | // If we have no source location, just emit the diagnostic message. |
99 | 16 | emitDiagnosticMessage(Loc, PresumedLoc(), Level, Message, Ranges, D); |
100 | 27.2k | else { |
101 | | // Get the ranges into a local array we can hack on. |
102 | 27.2k | SmallVector<CharSourceRange, 20> MutableRanges(Ranges.begin(), |
103 | 27.2k | Ranges.end()); |
104 | | |
105 | 27.2k | SmallVector<FixItHint, 8> MergedFixits; |
106 | 27.2k | if (!FixItHints.empty()) { |
107 | 2.97k | mergeFixits(FixItHints, Loc.getManager(), LangOpts, MergedFixits); |
108 | 2.97k | FixItHints = MergedFixits; |
109 | 2.97k | } |
110 | | |
111 | 27.2k | for (const auto &Hint : FixItHints) |
112 | 3.63k | if (Hint.RemoveRange.isValid()) |
113 | 3.63k | MutableRanges.push_back(Hint.RemoveRange); |
114 | | |
115 | 27.2k | FullSourceLoc UnexpandedLoc = Loc; |
116 | | |
117 | | // Find the ultimate expansion location for the diagnostic. |
118 | 27.2k | Loc = Loc.getFileLoc(); |
119 | | |
120 | 27.2k | 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 | 27.2k | emitIncludeStack(Loc, PLoc, Level); |
125 | | |
126 | | // Next, emit the actual diagnostic message and caret. |
127 | 27.2k | emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, D); |
128 | 27.2k | 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 | 27.2k | if (UnexpandedLoc.isValid() && UnexpandedLoc.isMacroID()) { |
133 | 476 | emitMacroExpansions(UnexpandedLoc, Level, MutableRanges, FixItHints); |
134 | 476 | } |
135 | 27.2k | } |
136 | | |
137 | 27.2k | LastLoc = Loc; |
138 | 27.2k | LastLevel = Level; |
139 | | |
140 | 27.2k | endDiagnostic(D, Level); |
141 | 27.2k | } |
142 | | |
143 | 638 | void DiagnosticRenderer::emitStoredDiagnostic(StoredDiagnostic &Diag) { |
144 | 638 | emitDiagnostic(Diag.getLocation(), Diag.getLevel(), Diag.getMessage(), |
145 | 638 | Diag.getRanges(), Diag.getFixIts(), |
146 | 638 | &Diag); |
147 | 638 | } |
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 | 27.2k | DiagnosticsEngine::Level Level) { |
167 | 27.2k | FullSourceLoc IncludeLoc = |
168 | 2 | PLoc.isInvalid() ? FullSourceLoc() |
169 | 27.2k | : FullSourceLoc(PLoc.getIncludeLoc(), Loc.getManager()); |
170 | | |
171 | | // Skip redundant include stacks altogether. |
172 | 27.2k | if (LastIncludeLoc == IncludeLoc) |
173 | 25.7k | return; |
174 | | |
175 | 1.50k | LastIncludeLoc = IncludeLoc; |
176 | | |
177 | 1.50k | if (!DiagOpts->ShowNoteIncludeStack && Level == DiagnosticsEngine::Note1.48k ) |
178 | 671 | return; |
179 | | |
180 | 830 | if (IncludeLoc.isValid()) |
181 | 337 | emitIncludeStackRecursively(IncludeLoc); |
182 | 493 | else { |
183 | 493 | emitModuleBuildStack(Loc.getManager()); |
184 | 493 | emitImportStack(Loc); |
185 | 493 | } |
186 | 830 | } |
187 | | |
188 | | /// Helper to recursively walk up the include stack and print each layer |
189 | | /// on the way back down. |
190 | 1.07k | void DiagnosticRenderer::emitIncludeStackRecursively(FullSourceLoc Loc) { |
191 | 1.07k | if (Loc.isInvalid()) { |
192 | 312 | emitModuleBuildStack(Loc.getManager()); |
193 | 312 | return; |
194 | 312 | } |
195 | | |
196 | 765 | PresumedLoc PLoc = Loc.getPresumedLoc(DiagOpts->ShowPresumedLoc); |
197 | 765 | 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 | 765 | std::pair<FullSourceLoc, StringRef> Imported = Loc.getModuleImportLoc(); |
204 | 765 | if (!Imported.second.empty()) { |
205 | | // This location was imported by a module. Emit the module import stack. |
206 | 25 | emitImportStackRecursively(Imported.first, Imported.second); |
207 | 25 | return; |
208 | 25 | } |
209 | | |
210 | | // Emit the other include frames first. |
211 | 740 | emitIncludeStackRecursively( |
212 | 740 | FullSourceLoc(PLoc.getIncludeLoc(), Loc.getManager())); |
213 | | |
214 | | // Emit the inclusion text/note. |
215 | 740 | emitIncludeLocation(Loc, PLoc); |
216 | 740 | } |
217 | | |
218 | | /// Emit the module import stack associated with the current location. |
219 | 493 | void DiagnosticRenderer::emitImportStack(FullSourceLoc Loc) { |
220 | 493 | if (Loc.isInvalid()) { |
221 | 0 | emitModuleBuildStack(Loc.getManager()); |
222 | 0 | return; |
223 | 0 | } |
224 | | |
225 | 493 | std::pair<FullSourceLoc, StringRef> NextImportLoc = Loc.getModuleImportLoc(); |
226 | 493 | emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second); |
227 | 493 | } |
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 | 544 | StringRef ModuleName) { |
233 | 544 | if (ModuleName.empty()) { |
234 | 518 | return; |
235 | 518 | } |
236 | | |
237 | 26 | PresumedLoc PLoc = Loc.getPresumedLoc(DiagOpts->ShowPresumedLoc); |
238 | | |
239 | | // Emit the other import frames first. |
240 | 26 | std::pair<FullSourceLoc, StringRef> NextImportLoc = Loc.getModuleImportLoc(); |
241 | 26 | emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second); |
242 | | |
243 | | // Emit the inclusion text/note. |
244 | 26 | emitImportLocation(Loc, PLoc, ModuleName); |
245 | 26 | } |
246 | | |
247 | | /// Emit the module build stack, for cases where a module is (re-)built |
248 | | /// on demand. |
249 | 805 | void DiagnosticRenderer::emitModuleBuildStack(const SourceManager &SM) { |
250 | 805 | ModuleBuildStack Stack = SM.getModuleBuildStack(); |
251 | 115 | for (const auto &I : Stack) { |
252 | 115 | emitBuildingModuleLocation(I.second, I.second.getPresumedLoc( |
253 | 115 | DiagOpts->ShowPresumedLoc), |
254 | 115 | I.first); |
255 | 115 | } |
256 | 805 | } |
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.1k | bool &IsTokenRange) { |
266 | 38.1k | assert(SM->getFileID(Loc) == MacroFileID); |
267 | 38.1k | if (MacroFileID == CaretFileID) |
268 | 32.4k | return Loc; |
269 | 5.73k | if (!Loc.isMacroID()) |
270 | 1.96k | return {}; |
271 | | |
272 | 3.77k | CharSourceRange MacroRange, MacroArgRange; |
273 | | |
274 | 3.77k | 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.47k | if (std::binary_search(CommonArgExpansions.begin(), |
278 | 1.47k | CommonArgExpansions.end(), MacroFileID)) |
279 | 1.45k | MacroRange = |
280 | 1.45k | CharSourceRange(SM->getImmediateSpellingLoc(Loc), IsTokenRange); |
281 | 1.47k | MacroArgRange = SM->getImmediateExpansionRange(Loc); |
282 | 2.29k | } else { |
283 | 2.29k | MacroRange = SM->getImmediateExpansionRange(Loc); |
284 | 2.29k | MacroArgRange = |
285 | 2.29k | CharSourceRange(SM->getImmediateSpellingLoc(Loc), IsTokenRange); |
286 | 2.29k | } |
287 | | |
288 | 3.77k | SourceLocation MacroLocation = |
289 | 1.89k | IsBegin ? MacroRange.getBegin()1.88k : MacroRange.getEnd(); |
290 | 3.77k | if (MacroLocation.isValid()) { |
291 | 3.75k | MacroFileID = SM->getFileID(MacroLocation); |
292 | 1.87k | bool TokenRange = IsBegin ? IsTokenRange1.87k : MacroRange.isTokenRange(); |
293 | 3.75k | MacroLocation = |
294 | 3.75k | retrieveMacroLocation(MacroLocation, MacroFileID, CaretFileID, |
295 | 3.75k | CommonArgExpansions, IsBegin, SM, TokenRange); |
296 | 3.75k | if (MacroLocation.isValid()) { |
297 | 2.54k | IsTokenRange = TokenRange; |
298 | 2.54k | return MacroLocation; |
299 | 2.54k | } |
300 | 1.22k | } |
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 | 640 | IsTokenRange = MacroArgRange.isTokenRange(); |
306 | | |
307 | 1.22k | SourceLocation MacroArgLocation = |
308 | 640 | IsBegin ? MacroArgRange.getBegin()587 : MacroArgRange.getEnd(); |
309 | 1.22k | MacroFileID = SM->getFileID(MacroArgLocation); |
310 | 1.22k | return retrieveMacroLocation(MacroArgLocation, MacroFileID, CaretFileID, |
311 | 1.22k | CommonArgExpansions, IsBegin, SM, IsTokenRange); |
312 | 1.22k | } |
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.2k | bool IsBegin, const SourceManager *SM) { |
319 | 39.1k | while (Loc.isMacroID()) { |
320 | 5.91k | if (SM->isMacroArgExpansion(Loc)) { |
321 | 1.86k | IDs.push_back(SM->getFileID(Loc)); |
322 | 1.86k | Loc = SM->getImmediateSpellingLoc(Loc); |
323 | 4.05k | } else { |
324 | 4.05k | auto ExpRange = SM->getImmediateExpansionRange(Loc); |
325 | 2.03k | Loc = IsBegin ? ExpRange.getBegin() : ExpRange.getEnd()2.01k ; |
326 | 4.05k | } |
327 | 5.91k | } |
328 | 33.2k | } |
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.6k | SmallVectorImpl<FileID> &CommonArgExpansions) { |
335 | 16.6k | SmallVector<FileID, 4> BeginArgExpansions; |
336 | 16.6k | SmallVector<FileID, 4> EndArgExpansions; |
337 | 16.6k | getMacroArgExpansionFileIDs(Begin, BeginArgExpansions, /*IsBegin=*/true, SM); |
338 | 16.6k | getMacroArgExpansionFileIDs(End, EndArgExpansions, /*IsBegin=*/false, SM); |
339 | 16.6k | llvm::sort(BeginArgExpansions); |
340 | 16.6k | llvm::sort(EndArgExpansions); |
341 | 16.6k | std::set_intersection(BeginArgExpansions.begin(), BeginArgExpansions.end(), |
342 | 16.6k | EndArgExpansions.begin(), EndArgExpansions.end(), |
343 | 16.6k | std::back_inserter(CommonArgExpansions)); |
344 | 16.6k | } |
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 | 28.3k | SmallVectorImpl<CharSourceRange> &SpellingRanges) { |
359 | 28.3k | FileID CaretLocFileID = CaretLoc.getFileID(); |
360 | | |
361 | 28.3k | const SourceManager *SM = &CaretLoc.getManager(); |
362 | | |
363 | 18.4k | for (const auto &Range : Ranges) { |
364 | 18.4k | if (Range.isInvalid()) |
365 | 1.85k | continue; |
366 | | |
367 | 16.6k | SourceLocation Begin = Range.getBegin(), End = Range.getEnd(); |
368 | 16.6k | bool IsTokenRange = Range.isTokenRange(); |
369 | | |
370 | 16.6k | FileID BeginFileID = SM->getFileID(Begin); |
371 | 16.6k | 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.6k | llvm::SmallDenseMap<FileID, SourceLocation> BeginLocsMap; |
377 | 17.0k | while (Begin.isMacroID() && BeginFileID != EndFileID1.41k ) { |
378 | 393 | BeginLocsMap[BeginFileID] = Begin; |
379 | 393 | Begin = SM->getImmediateExpansionRange(Begin).getBegin(); |
380 | 393 | BeginFileID = SM->getFileID(Begin); |
381 | 393 | } |
382 | | |
383 | | // Then, crawl the expansion chain for the end of the range. |
384 | 16.6k | if (BeginFileID != EndFileID) { |
385 | 420 | while (End.isMacroID() && !BeginLocsMap.count(EndFileID)385 ) { |
386 | 225 | auto Exp = SM->getImmediateExpansionRange(End); |
387 | 225 | IsTokenRange = Exp.isTokenRange(); |
388 | 225 | End = Exp.getEnd(); |
389 | 225 | EndFileID = SM->getFileID(End); |
390 | 225 | } |
391 | 195 | if (End.isMacroID()) { |
392 | 160 | Begin = BeginLocsMap[EndFileID]; |
393 | 160 | BeginFileID = EndFileID; |
394 | 160 | } |
395 | 195 | } |
396 | | |
397 | | // Do the backtracking. |
398 | 16.6k | SmallVector<FileID, 4> CommonArgExpansions; |
399 | 16.6k | computeCommonMacroArgExpansionFileIDs(Begin, End, SM, CommonArgExpansions); |
400 | 16.6k | Begin = retrieveMacroLocation(Begin, BeginFileID, CaretLocFileID, |
401 | 16.6k | CommonArgExpansions, /*IsBegin=*/true, SM, |
402 | 16.6k | IsTokenRange); |
403 | 16.6k | End = retrieveMacroLocation(End, BeginFileID, CaretLocFileID, |
404 | 16.6k | CommonArgExpansions, /*IsBegin=*/false, SM, |
405 | 16.6k | IsTokenRange); |
406 | 16.6k | if (Begin.isInvalid() || End.isInvalid()16.2k ) continue391 ; |
407 | | |
408 | | // Return the spelling location of the beginning and end of the range. |
409 | 16.2k | Begin = SM->getSpellingLoc(Begin); |
410 | 16.2k | End = SM->getSpellingLoc(End); |
411 | | |
412 | 16.2k | SpellingRanges.push_back(CharSourceRange(SourceRange(Begin, End), |
413 | 16.2k | IsTokenRange)); |
414 | 16.2k | } |
415 | 28.3k | } |
416 | | |
417 | | void DiagnosticRenderer::emitCaret(FullSourceLoc Loc, |
418 | | DiagnosticsEngine::Level Level, |
419 | | ArrayRef<CharSourceRange> Ranges, |
420 | 27.2k | ArrayRef<FixItHint> Hints) { |
421 | 27.2k | SmallVector<CharSourceRange, 4> SpellingRanges; |
422 | 27.2k | mapDiagnosticRanges(Loc, Ranges, SpellingRanges); |
423 | 27.2k | emitCodeContext(Loc, Level, SpellingRanges, Hints); |
424 | 27.2k | } |
425 | | |
426 | | /// A helper function for emitMacroExpansion to print the |
427 | | /// macro expansion message |
428 | | void DiagnosticRenderer::emitSingleMacroExpansion( |
429 | | FullSourceLoc Loc, DiagnosticsEngine::Level Level, |
430 | 462 | ArrayRef<CharSourceRange> Ranges) { |
431 | | // Find the spelling location for the macro definition. We must use the |
432 | | // spelling location here to avoid emitting a macro backtrace for the note. |
433 | 462 | FullSourceLoc SpellingLoc = Loc.getSpellingLoc(); |
434 | | |
435 | | // Map the ranges into the FileID of the diagnostic location. |
436 | 462 | SmallVector<CharSourceRange, 4> SpellingRanges; |
437 | 462 | mapDiagnosticRanges(Loc, Ranges, SpellingRanges); |
438 | | |
439 | 462 | SmallString<100> MessageStorage; |
440 | 462 | llvm::raw_svector_ostream Message(MessageStorage); |
441 | 462 | StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics( |
442 | 462 | Loc, Loc.getManager(), LangOpts); |
443 | 462 | if (MacroName.empty()) |
444 | 50 | Message << "expanded from here"; |
445 | 412 | else |
446 | 412 | Message << "expanded from macro '" << MacroName << "'"; |
447 | | |
448 | 462 | emitDiagnostic(SpellingLoc, DiagnosticsEngine::Note, Message.str(), |
449 | 462 | SpellingRanges, None); |
450 | 462 | } |
451 | | |
452 | | /// Check that the macro argument location of Loc starts with ArgumentLoc. |
453 | | /// The starting location of the macro expansions is used to differeniate |
454 | | /// different macro expansions. |
455 | | static bool checkLocForMacroArgExpansion(SourceLocation Loc, |
456 | | const SourceManager &SM, |
457 | 54 | SourceLocation ArgumentLoc) { |
458 | 54 | SourceLocation MacroLoc; |
459 | 54 | if (SM.isMacroArgExpansion(Loc, &MacroLoc)) { |
460 | 0 | if (ArgumentLoc == MacroLoc) return true; |
461 | 54 | } |
462 | | |
463 | 54 | return false; |
464 | 54 | } |
465 | | |
466 | | /// Check if all the locations in the range have the same macro argument |
467 | | /// expansion, and that the expansion starts with ArgumentLoc. |
468 | | static bool checkRangeForMacroArgExpansion(CharSourceRange Range, |
469 | | const SourceManager &SM, |
470 | 54 | SourceLocation ArgumentLoc) { |
471 | 54 | SourceLocation BegLoc = Range.getBegin(), EndLoc = Range.getEnd(); |
472 | 54 | while (BegLoc != EndLoc) { |
473 | 8 | if (!checkLocForMacroArgExpansion(BegLoc, SM, ArgumentLoc)) |
474 | 8 | return false; |
475 | 0 | BegLoc.getLocWithOffset(1); |
476 | 0 | } |
477 | | |
478 | 46 | return checkLocForMacroArgExpansion(BegLoc, SM, ArgumentLoc); |
479 | 54 | } |
480 | | |
481 | | /// A helper function to check if the current ranges are all inside the same |
482 | | /// macro argument expansion as Loc. |
483 | | static bool checkRangesForMacroArgExpansion(FullSourceLoc Loc, |
484 | 667 | ArrayRef<CharSourceRange> Ranges) { |
485 | 667 | assert(Loc.isMacroID() && "Must be a macro expansion!"); |
486 | | |
487 | 667 | SmallVector<CharSourceRange, 4> SpellingRanges; |
488 | 667 | mapDiagnosticRanges(Loc, Ranges, SpellingRanges); |
489 | | |
490 | | /// Count all valid ranges. |
491 | 667 | unsigned ValidCount = 0; |
492 | 667 | for (const auto &Range : Ranges) |
493 | 622 | if (Range.isValid()) |
494 | 580 | ValidCount++; |
495 | | |
496 | 667 | if (ValidCount > SpellingRanges.size()) |
497 | 162 | return false; |
498 | | |
499 | | /// To store the source location of the argument location. |
500 | 505 | FullSourceLoc ArgumentLoc; |
501 | | |
502 | | /// Set the ArgumentLoc to the beginning location of the expansion of Loc |
503 | | /// so to check if the ranges expands to the same beginning location. |
504 | 505 | if (!Loc.isMacroArgExpansion(&ArgumentLoc)) |
505 | 267 | return false; |
506 | | |
507 | 238 | for (const auto &Range : SpellingRanges) |
508 | 54 | if (!checkRangeForMacroArgExpansion(Range, Loc.getManager(), ArgumentLoc)) |
509 | 54 | return false; |
510 | | |
511 | 184 | return true; |
512 | 238 | } |
513 | | |
514 | | /// Recursively emit notes for each macro expansion and caret |
515 | | /// diagnostics where appropriate. |
516 | | /// |
517 | | /// Walks up the macro expansion stack printing expansion notes, the code |
518 | | /// snippet, caret, underlines and FixItHint display as appropriate at each |
519 | | /// level. |
520 | | /// |
521 | | /// \param Loc The location for this caret. |
522 | | /// \param Level The diagnostic level currently being emitted. |
523 | | /// \param Ranges The underlined ranges for this code snippet. |
524 | | /// \param Hints The FixIt hints active for this diagnostic. |
525 | | void DiagnosticRenderer::emitMacroExpansions(FullSourceLoc Loc, |
526 | | DiagnosticsEngine::Level Level, |
527 | | ArrayRef<CharSourceRange> Ranges, |
528 | 476 | ArrayRef<FixItHint> Hints) { |
529 | 476 | assert(Loc.isValid() && "must have a valid source location here"); |
530 | 476 | const SourceManager &SM = Loc.getManager(); |
531 | 476 | SourceLocation L = Loc; |
532 | | |
533 | | // Produce a stack of macro backtraces. |
534 | 476 | SmallVector<SourceLocation, 8> LocationStack; |
535 | 476 | unsigned IgnoredEnd = 0; |
536 | 1.14k | while (L.isMacroID()) { |
537 | | // If this is the expansion of a macro argument, point the caret at the |
538 | | // use of the argument in the definition of the macro, not the expansion. |
539 | 667 | if (SM.isMacroArgExpansion(L)) |
540 | 317 | LocationStack.push_back(SM.getImmediateExpansionRange(L).getBegin()); |
541 | 350 | else |
542 | 350 | LocationStack.push_back(L); |
543 | | |
544 | 667 | if (checkRangesForMacroArgExpansion(FullSourceLoc(L, SM), Ranges)) |
545 | 184 | IgnoredEnd = LocationStack.size(); |
546 | | |
547 | 667 | L = SM.getImmediateMacroCallerLoc(L); |
548 | | |
549 | | // Once the location no longer points into a macro, try stepping through |
550 | | // the last found location. This sometimes produces additional useful |
551 | | // backtraces. |
552 | 667 | if (L.isFileID()) |
553 | 479 | L = SM.getImmediateMacroCallerLoc(LocationStack.back()); |
554 | 667 | assert(L.isValid() && "must have a valid source location here"); |
555 | 667 | } |
556 | | |
557 | 476 | LocationStack.erase(LocationStack.begin(), |
558 | 476 | LocationStack.begin() + IgnoredEnd); |
559 | | |
560 | 476 | unsigned MacroDepth = LocationStack.size(); |
561 | 476 | unsigned MacroLimit = DiagOpts->MacroBacktraceLimit; |
562 | 476 | if (MacroDepth <= MacroLimit || MacroLimit == 07 ) { |
563 | 472 | for (auto I = LocationStack.rbegin(), E = LocationStack.rend(); |
564 | 915 | I != E; ++I443 ) |
565 | 443 | emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges); |
566 | 472 | return; |
567 | 472 | } |
568 | | |
569 | 4 | unsigned MacroStartMessages = MacroLimit / 2; |
570 | 4 | unsigned MacroEndMessages = MacroLimit / 2 + MacroLimit % 2; |
571 | | |
572 | 4 | for (auto I = LocationStack.rbegin(), |
573 | 4 | E = LocationStack.rbegin() + MacroStartMessages; |
574 | 13 | I != E; ++I9 ) |
575 | 9 | emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges); |
576 | | |
577 | 4 | SmallString<200> MessageStorage; |
578 | 4 | llvm::raw_svector_ostream Message(MessageStorage); |
579 | 4 | Message << "(skipping " << (MacroDepth - MacroLimit) |
580 | 4 | << " expansions in backtrace; use -fmacro-backtrace-limit=0 to " |
581 | 4 | "see all)"; |
582 | 4 | emitBasicNote(Message.str()); |
583 | | |
584 | 4 | for (auto I = LocationStack.rend() - MacroEndMessages, |
585 | 4 | E = LocationStack.rend(); |
586 | 14 | I != E; ++I10 ) |
587 | 10 | emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges); |
588 | 4 | } |
589 | | |
590 | 556 | DiagnosticNoteRenderer::~DiagnosticNoteRenderer() = default; |
591 | | |
592 | | void DiagnosticNoteRenderer::emitIncludeLocation(FullSourceLoc Loc, |
593 | 239 | PresumedLoc PLoc) { |
594 | | // Generate a note indicating the include location. |
595 | 239 | SmallString<200> MessageStorage; |
596 | 239 | llvm::raw_svector_ostream Message(MessageStorage); |
597 | 239 | Message << "in file included from " << PLoc.getFilename() << ':' |
598 | 239 | << PLoc.getLine() << ":"; |
599 | 239 | emitNote(Loc, Message.str()); |
600 | 239 | } |
601 | | |
602 | | void DiagnosticNoteRenderer::emitImportLocation(FullSourceLoc Loc, |
603 | | PresumedLoc PLoc, |
604 | 0 | StringRef ModuleName) { |
605 | | // Generate a note indicating the include location. |
606 | 0 | SmallString<200> MessageStorage; |
607 | 0 | llvm::raw_svector_ostream Message(MessageStorage); |
608 | 0 | Message << "in module '" << ModuleName; |
609 | 0 | if (PLoc.isValid()) |
610 | 0 | Message << "' imported from " << PLoc.getFilename() << ':' |
611 | 0 | << PLoc.getLine(); |
612 | 0 | Message << ":"; |
613 | 0 | emitNote(Loc, Message.str()); |
614 | 0 | } |
615 | | |
616 | | void DiagnosticNoteRenderer::emitBuildingModuleLocation(FullSourceLoc Loc, |
617 | | PresumedLoc PLoc, |
618 | 20 | StringRef ModuleName) { |
619 | | // Generate a note indicating the include location. |
620 | 20 | SmallString<200> MessageStorage; |
621 | 20 | llvm::raw_svector_ostream Message(MessageStorage); |
622 | 20 | if (PLoc.isValid()) |
623 | 20 | Message << "while building module '" << ModuleName << "' imported from " |
624 | 20 | << PLoc.getFilename() << ':' << PLoc.getLine() << ":"; |
625 | 0 | else |
626 | 0 | Message << "while building module '" << ModuleName << "':"; |
627 | 20 | emitNote(Loc, Message.str()); |
628 | 20 | } |