clang: lib/Frontend/DiagnosticRenderer.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
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
25#include
26#include
27#include
28
29using namespace clang;
30
34
36
37namespace {
38
41
42public:
44 : MergedFixits(MergedFixits) {}
45
46 void insert(SourceLocation loc, StringRef text) override {
48 }
49
50 void replace(CharSourceRange range, StringRef text) override {
52 }
53};
54
55}
56
61 for (const auto &Hint : FixItHints)
62 if (Hint.CodeToInsert.empty()) {
63 if (Hint.InsertFromRange.isValid())
65 Hint.InsertFromRange, false,
66 Hint.BeforePreviousInsertions);
67 else
68 commit.remove(Hint.RemoveRange);
69 } else {
70 if (Hint.RemoveRange.isTokenRange() ||
71 Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd())
72 commit.replace(Hint.RemoveRange, Hint.CodeToInsert);
73 else
74 commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert,
75 false, Hint.BeforePreviousInsertions);
76 }
77
79 if (Editor.commit(commit)) {
80 FixitReceiver Rec(MergedFixits);
82 }
83}
84
87 StringRef Message,
92
94
96
98 else {
99
101
103 if (!FixItHints.empty()) {
105 FixItHints = MergedFixits;
106 }
107
108 for (const auto &Hint : FixItHints)
109 if (Hint.RemoveRange.isValid())
110 MutableRanges.push_back(Hint.RemoveRange);
111
113
114
116
118
119
120
121 emitIncludeStack(Loc, PLoc, Level);
122
123
125 emitCaret(Loc, Level, MutableRanges, FixItHints);
126
127
128
130 emitMacroExpansions(UnexpandedLoc, Level, MutableRanges, FixItHints);
131 }
132 }
133
136
138}
139
142 Diag.getRanges(), Diag.getFixIts(),
144}
145
146void DiagnosticRenderer::emitBasicNote(StringRef Message) {
149}
150
151
152
153
154
155
156
157
158
159
160
161
164 FullSourceLoc IncludeLoc =
165 PLoc.isInvalid() ? FullSourceLoc()
166 : FullSourceLoc(PLoc.getIncludeLoc(), Loc.getManager());
167
168
170 return;
171
173
175 return;
176
178 emitIncludeStackRecursively(IncludeLoc);
179 else {
180 emitModuleBuildStack(Loc.getManager());
181 emitImportStack(Loc);
182 }
183}
184
185
186
187void DiagnosticRenderer::emitIncludeStackRecursively(FullSourceLoc Loc) {
189 emitModuleBuildStack(Loc.getManager());
190 return;
191 }
192
195 return;
196
197
198
199
200 std::pair<FullSourceLoc, StringRef> Imported = Loc.getModuleImportLoc();
201 if (!Imported.second.empty()) {
202
203 emitImportStackRecursively(Imported.first, Imported.second);
204 return;
205 }
206
207
208 emitIncludeStackRecursively(
210
211
213}
214
215
216void DiagnosticRenderer::emitImportStack(FullSourceLoc Loc) {
218 emitModuleBuildStack(Loc.getManager());
219 return;
220 }
221
222 std::pair<FullSourceLoc, StringRef> NextImportLoc = Loc.getModuleImportLoc();
223 emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second);
224}
225
226
227
228void DiagnosticRenderer::emitImportStackRecursively(FullSourceLoc Loc,
229 StringRef ModuleName) {
230 if (ModuleName.empty()) {
231 return;
232 }
233
235
236
237 std::pair<FullSourceLoc, StringRef> NextImportLoc = Loc.getModuleImportLoc();
238 emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second);
239
240
242}
243
244
245
246void DiagnosticRenderer::emitModuleBuildStack(const SourceManager &SM) {
248 for (const auto &I : Stack) {
250 I.second, I.second.getPresumedLoc(DiagOpts.ShowPresumedLoc), I.first);
251 }
252}
253
254
255
261 bool &IsTokenRange) {
262 assert(SM->getFileID(Loc) == MacroFileID);
263 if (MacroFileID == CaretFileID)
264 return Loc;
266 return {};
267
269
270 if (SM->isMacroArgExpansion(Loc)) {
271
272
273 if (llvm::binary_search(CommonArgExpansions, MacroFileID))
274 MacroRange =
276 MacroArgRange = SM->getImmediateExpansionRange(Loc);
277 } else {
278 MacroRange = SM->getImmediateExpansionRange(Loc);
279 MacroArgRange =
281 }
282
284 IsBegin ? MacroRange.getBegin() : MacroRange.getEnd();
285 if (MacroLocation.isValid()) {
286 MacroFileID = SM->getFileID(MacroLocation);
287 bool TokenRange = IsBegin ? IsTokenRange : MacroRange.isTokenRange();
288 MacroLocation =
290 CommonArgExpansions, IsBegin, SM, TokenRange);
291 if (MacroLocation.isValid()) {
292 IsTokenRange = TokenRange;
293 return MacroLocation;
294 }
295 }
296
297
298
299 if (!IsBegin)
301
303 IsBegin ? MacroArgRange.getBegin() : MacroArgRange.getEnd();
304 MacroFileID = SM->getFileID(MacroArgLocation);
306 CommonArgExpansions, IsBegin, SM, IsTokenRange);
307}
308
309
310
315 if (SM->isMacroArgExpansion(Loc)) {
316 IDs.push_back(SM->getFileID(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
326
334 llvm::sort(BeginArgExpansions);
335 llvm::sort(EndArgExpansions);
336 std::set_intersection(BeginArgExpansions.begin(), BeginArgExpansions.end(),
337 EndArgExpansions.begin(), EndArgExpansions.end(),
338 std::back_inserter(CommonArgExpansions));
339}
340
341
342
343
344
345
346
347
348
349
350
351static void
355
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(Begin);
366 FileID EndFileID = SM->getFileID(End);
367
368
369
370
371 llvm::SmallDenseMap<FileID, SourceLocation> BeginLocsMap;
372 while (Begin.isMacroID() && BeginFileID != EndFileID) {
373 BeginLocsMap[BeginFileID] = Begin;
374 Begin = SM->getImmediateExpansionRange(Begin).getBegin();
375 BeginFileID = SM->getFileID(Begin);
376 }
377
378
379 if (BeginFileID != EndFileID) {
380 while (End.isMacroID() && !BeginLocsMap.count(EndFileID)) {
381 auto Exp = SM->getImmediateExpansionRange(End);
382 IsTokenRange = Exp.isTokenRange();
383 End = Exp.getEnd();
384 EndFileID = SM->getFileID(End);
385 }
386 if (End.isMacroID()) {
387 Begin = BeginLocsMap[EndFileID];
388 BeginFileID = EndFileID;
389 }
390 }
391
392
393
394
395
396 if (Begin.isInvalid() || End.isInvalid() || BeginFileID != EndFileID)
397 continue;
398
399
403 CommonArgExpansions, true, SM,
404 IsTokenRange);
406 CommonArgExpansions, false, SM,
407 IsTokenRange);
408 if (Begin.isInvalid() || End.isInvalid()) continue;
409
410
411 Begin = SM->getSpellingLoc(Begin);
412 End = SM->getSpellingLoc(End);
413
415 IsTokenRange));
416 }
417}
418
419void DiagnosticRenderer::emitCaret(FullSourceLoc Loc,
423 SmallVector<CharSourceRange, 4> SpellingRanges;
426}
427
428
429
430void DiagnosticRenderer::emitSingleMacroExpansion(
433
434
436
437
438 SmallVector<CharSourceRange, 4> SpellingRanges;
440
441 SmallString<100> MessageStorage;
442 llvm::raw_svector_ostream Message(MessageStorage);
445 if (MacroName.empty())
446 Message << "expanded from here";
447 else
448 Message << "expanded from macro '" << MacroName << "'";
449
451 SpellingRanges, {});
452}
453
454
455
456static bool
459 assert(Loc.isMacroID() && "Must be a macro expansion!");
460
463
464 unsigned ValidCount =
465 llvm::count_if(Ranges, [](const auto &R) { return R.isValid(); });
466 if (ValidCount > SpellingRanges.size())
467 return false;
468
470 for (const auto &R : Ranges) {
471
473 if (Begin == R.getEnd()) {
474 if (.isMacroArgExpansion(Begin))
475 return false;
476 continue;
477 }
478
479 while (Begin != R.getEnd()) {
481 if (.isMacroArgExpansion(Begin, &MacroLoc))
482 return false;
483 if (MacroLoc != Loc)
484 return false;
485
487 }
488 }
489
490 return true;
491}
492
493
494
495
496
497
498
499
500
501
502
503
504void DiagnosticRenderer::emitMacroExpansions(FullSourceLoc Loc,
508 assert(Loc.isValid() && "must have a valid source location here");
510 SourceLocation L = Loc;
511
512
513 SmallVector<SourceLocation, 8> LocationStack;
514 unsigned IgnoredEnd = 0;
516
517
518 if (SM.isMacroArgExpansion(L)) {
519 LocationStack.push_back(SM.getImmediateExpansionRange(L).getBegin());
520
522 IgnoredEnd = LocationStack.size();
523 } else
524 LocationStack.push_back(L);
525
526 L = SM.getImmediateMacroCallerLoc(L);
527
528
529
530
532 L = SM.getImmediateMacroCallerLoc(LocationStack.back());
533 assert(L.isValid() && "must have a valid source location here");
534 }
535
536 LocationStack.erase(LocationStack.begin(),
537 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(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(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.str());
562
563 for (auto I = LocationStack.rend() - MacroEndMessages,
564 E = LocationStack.rend();
565 I != E; ++I)
566 emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges);
567}
568
570
573
575 llvm::raw_svector_ostream Message(MessageStorage);
576 Message << "in file included from " << PLoc.getFilename() << ':'
577 << PLoc.getLine() << ":";
578 emitNote(Loc, Message.str());
579}
580
583 StringRef ModuleName) {
584
586 llvm::raw_svector_ostream Message(MessageStorage);
587 Message << "in module '" << ModuleName;
589 Message << "' imported from " << PLoc.getFilename() << ':'
591 Message << ":";
592 emitNote(Loc, Message.str());
593}
594
597 StringRef ModuleName) {
598
600 llvm::raw_svector_ostream Message(MessageStorage);
602 Message << "while building module '" << ModuleName << "' imported from "
604 else
605 Message << "while building module '" << ModuleName << "':";
606 emitNote(Loc, Message.str());
607}
Defines the Diagnostic-related interfaces.
static void getMacroArgExpansionFileIDs(SourceLocation Loc, SmallVectorImpl< FileID > &IDs, bool IsBegin, const SourceManager *SM)
Walk up the chain of macro expansions and collect the FileIDs identifying the expansions.
Definition DiagnosticRenderer.cpp:311
static SourceLocation retrieveMacroLocation(SourceLocation Loc, FileID MacroFileID, FileID CaretFileID, const SmallVectorImpl< FileID > &CommonArgExpansions, bool IsBegin, const SourceManager *SM, bool &IsTokenRange)
A recursive function to trace all possible backtrace locations to match the CaretLocFileID.
Definition DiagnosticRenderer.cpp:257
static void computeCommonMacroArgExpansionFileIDs(SourceLocation Begin, SourceLocation End, const SourceManager *SM, SmallVectorImpl< FileID > &CommonArgExpansions)
Collect the expansions of the begin and end locations and compute the set intersection.
Definition DiagnosticRenderer.cpp:327
static void mapDiagnosticRanges(FullSourceLoc CaretLoc, ArrayRef< CharSourceRange > Ranges, SmallVectorImpl< CharSourceRange > &SpellingRanges)
Definition DiagnosticRenderer.cpp:352
static void mergeFixits(ArrayRef< FixItHint > FixItHints, const SourceManager &SM, const LangOptions &LangOpts, SmallVectorImpl< FixItHint > &MergedFixits)
Definition DiagnosticRenderer.cpp:57
static bool rangesInsideSameMacroArgExpansion(FullSourceLoc Loc, ArrayRef< CharSourceRange > Ranges)
A helper function to check if the current ranges are all inside the same macro argument expansion as ...
Definition DiagnosticRenderer.cpp:457
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
static DiagnosticBuilder Diag(DiagnosticsEngine *Diags, const LangOptions &Features, FullSourceLoc TokLoc, const char *TokBegin, const char *TokRangeBegin, const char *TokRangeEnd, unsigned DiagID)
Produce a diagnostic highlighting some portion of a literal.
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Represents a character-granular source range.
bool isTokenRange() const
Return true if the end of this range specifies the start of the last token.
SourceLocation getEnd() const
SourceLocation getBegin() const
~DiagnosticNoteRenderer() override
virtual void emitNote(FullSourceLoc Loc, StringRef Message)=0
void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc, StringRef ModuleName) override
Definition DiagnosticRenderer.cpp:595
void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) override
Definition DiagnosticRenderer.cpp:571
void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc, StringRef ModuleName) override
Definition DiagnosticRenderer.cpp:581
Options for controlling the compiler diagnostics engine.
virtual void endDiagnostic(DiagOrStoredDiag D, DiagnosticsEngine::Level Level)
virtual void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc)=0
virtual ~DiagnosticRenderer()
const LangOptions & LangOpts
void emitStoredDiagnostic(StoredDiagnostic &Diag)
Definition DiagnosticRenderer.cpp:140
SourceLocation LastLoc
The location of the previous diagnostic if known.
DiagnosticOptions & DiagOpts
DiagnosticsEngine::Level LastLevel
The level of the last diagnostic emitted.
virtual void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc, StringRef ModuleName)=0
virtual void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, StringRef Message, ArrayRef< CharSourceRange > Ranges, DiagOrStoredDiag Info)=0
SourceLocation LastIncludeLoc
The location of the last include whose stack was printed if known.
virtual void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc, StringRef ModuleName)=0
void emitDiagnostic(FullSourceLoc Loc, DiagnosticsEngine::Level Level, StringRef Message, ArrayRef< CharSourceRange > Ranges, ArrayRef< FixItHint > FixItHints, DiagOrStoredDiag D=(Diagnostic *) nullptr)
Emit a diagnostic.
Definition DiagnosticRenderer.cpp:85
virtual void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level, SmallVectorImpl< CharSourceRange > &Ranges, ArrayRef< FixItHint > Hints)=0
DiagnosticRenderer(const LangOptions &LangOpts, DiagnosticOptions &DiagOpts)
Definition DiagnosticRenderer.cpp:31
virtual void beginDiagnostic(DiagOrStoredDiag D, DiagnosticsEngine::Level Level)
Level
The level of the diagnostic, after it has been through mapping.
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
static FixItHint CreateReplacement(CharSourceRange RemoveRange, StringRef Code)
Create a code modification hint that replaces the given source range with the given code string.
static FixItHint CreateInsertion(SourceLocation InsertionLoc, StringRef Code, bool BeforePreviousInsertions=false)
Create a code modification hint that inserts the given code string at a specific location.
A SourceLocation and its associated SourceManager.
FullSourceLoc getFileLoc() const
FullSourceLoc getSpellingLoc() const
std::pair< FullSourceLoc, StringRef > getModuleImportLoc() const
PresumedLoc getPresumedLoc(bool UseLineDirectives=true) const
bool hasManager() const
Checks whether the SourceManager is present.
const SourceManager & getManager() const
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
static StringRef getImmediateMacroNameForDiagnostics(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
Retrieve the name of the immediate macro expansion.
Represents an unpacked "presumed" location which can be presented to the user.
const char * getFilename() const
Return the presumed filename of this location.
unsigned getLine() const
Return the presumed line number of this location.
bool isInvalid() const
Return true if this object is invalid or uninitialized.
SourceLocation getIncludeLoc() const
Return the presumed include location of this location.
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
Represents a diagnostic in a form that can be retained until its corresponding source manager is dest...
bool insertFromRange(SourceLocation loc, CharSourceRange range, bool afterToken=false, bool beforePreviousInsertions=false)
bool insert(SourceLocation loc, StringRef text, bool afterToken=false, bool beforePreviousInsertions=false)
bool remove(CharSourceRange range)
bool replace(CharSourceRange range, StringRef text)
void applyRewrites(EditsReceiver &receiver, bool adjustRemovals=true)
bool commit(const Commit &commit)
The JSON file list parser is used to communicate input to InstallAPI.
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
llvm::PointerUnion< const Diagnostic *, const StoredDiagnostic * > DiagOrStoredDiag
ArrayRef< std::pair< std::string, FullSourceLoc > > ModuleBuildStack
The stack used when building modules on demand, which is used to provide a link between the source ma...