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