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/SmallString.h"
22#include "llvm/ADT/SmallVector.h"
23#include "llvm/ADT/StringRef.h"
24#include "llvm/Support/raw_ostream.h"
25#include
26#include
27#include
28#include
29
30using namespace clang;
31
34 : LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {}
35
37
38namespace {
39
42
43public:
45 : MergedFixits(MergedFixits) {}
46
47 void insert(SourceLocation loc, StringRef text) override {
49 }
50
51 void replace(CharSourceRange range, StringRef text) override {
53 }
54};
55
56}
57
62 for (const auto &Hint : FixItHints)
63 if (Hint.CodeToInsert.empty()) {
64 if (Hint.InsertFromRange.isValid())
66 Hint.InsertFromRange, false,
67 Hint.BeforePreviousInsertions);
68 else
69 commit.remove(Hint.RemoveRange);
70 } else {
71 if (Hint.RemoveRange.isTokenRange() ||
72 Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd())
73 commit.replace(Hint.RemoveRange, Hint.CodeToInsert);
74 else
75 commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert,
76 false, Hint.BeforePreviousInsertions);
77 }
78
80 if (Editor.commit(commit)) {
81 FixitReceiver Rec(MergedFixits);
83 }
84}
85
88 StringRef Message,
93
95
97
99 else {
100
102
104 if (!FixItHints.empty()) {
106 FixItHints = MergedFixits;
107 }
108
109 for (const auto &Hint : FixItHints)
110 if (Hint.RemoveRange.isValid())
111 MutableRanges.push_back(Hint.RemoveRange);
112
114
115
117
119
120
121
122 emitIncludeStack(Loc, PLoc, Level);
123
124
126 emitCaret(Loc, Level, MutableRanges, FixItHints);
127
128
129
131 emitMacroExpansions(UnexpandedLoc, Level, MutableRanges, FixItHints);
132 }
133 }
134
137
139}
140
143 Diag.getRanges(), Diag.getFixIts(),
145}
146
147void DiagnosticRenderer::emitBasicNote(StringRef Message) {
150}
151
152
153
154
155
156
157
158
159
160
161
162
168
169
171 return;
172
174
176 return;
177
178 if (IncludeLoc.isValid())
179 emitIncludeStackRecursively(IncludeLoc);
180 else {
181 emitModuleBuildStack(Loc.getManager());
182 emitImportStack(Loc);
183 }
184}
185
186
187
188void DiagnosticRenderer::emitIncludeStackRecursively(FullSourceLoc Loc) {
190 emitModuleBuildStack(Loc.getManager());
191 return;
192 }
193
196 return;
197
198
199
200
201 std::pair<FullSourceLoc, StringRef> Imported = Loc.getModuleImportLoc();
202 if (!Imported.second.empty()) {
203
204 emitImportStackRecursively(Imported.first, Imported.second);
205 return;
206 }
207
208
209 emitIncludeStackRecursively(
211
212
214}
215
216
219 emitModuleBuildStack(Loc.getManager());
220 return;
221 }
222
223 std::pair<FullSourceLoc, StringRef> NextImportLoc = Loc.getModuleImportLoc();
224 emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second);
225}
226
227
228
229void DiagnosticRenderer::emitImportStackRecursively(FullSourceLoc Loc,
230 StringRef ModuleName) {
231 if (ModuleName.empty()) {
232 return;
233 }
234
236
237
238 std::pair<FullSourceLoc, StringRef> NextImportLoc = Loc.getModuleImportLoc();
239 emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second);
240
241
243}
244
245
246
247void DiagnosticRenderer::emitModuleBuildStack(const SourceManager &SM) {
249 for (const auto &I : Stack) {
252 I.first);
253 }
254}
255
256
257
263 bool &IsTokenRange) {
264 assert(SM->getFileID(Loc) == MacroFileID);
265 if (MacroFileID == CaretFileID)
266 return Loc;
268 return {};
269
271
272 if (SM->isMacroArgExpansion(Loc)) {
273
274
275 if (std::binary_search(CommonArgExpansions.begin(),
276 CommonArgExpansions.end(), MacroFileID))
277 MacroRange =
279 MacroArgRange = SM->getImmediateExpansionRange(Loc);
280 } else {
281 MacroRange = SM->getImmediateExpansionRange(Loc);
282 MacroArgRange =
284 }
285
287 IsBegin ? MacroRange.getBegin() : MacroRange.getEnd();
288 if (MacroLocation.isValid()) {
289 MacroFileID = SM->getFileID(MacroLocation);
290 bool TokenRange = IsBegin ? IsTokenRange : MacroRange.isTokenRange();
291 MacroLocation =
293 CommonArgExpansions, IsBegin, SM, TokenRange);
294 if (MacroLocation.isValid()) {
295 IsTokenRange = TokenRange;
296 return MacroLocation;
297 }
298 }
299
300
301
302 if (!IsBegin)
304
306 IsBegin ? MacroArgRange.getBegin() : MacroArgRange.getEnd();
307 MacroFileID = SM->getFileID(MacroArgLocation);
309 CommonArgExpansions, IsBegin, SM, IsTokenRange);
310}
311
312
313
318 if (SM->isMacroArgExpansion(Loc)) {
319 IDs.push_back(SM->getFileID(Loc));
320 Loc = SM->getImmediateSpellingLoc(Loc);
321 } else {
322 auto ExpRange = SM->getImmediateExpansionRange(Loc);
323 Loc = IsBegin ? ExpRange.getBegin() : ExpRange.getEnd();
324 }
325 }
326}
327
328
329
337 llvm::sort(BeginArgExpansions);
338 llvm::sort(EndArgExpansions);
339 std::set_intersection(BeginArgExpansions.begin(), BeginArgExpansions.end(),
340 EndArgExpansions.begin(), EndArgExpansions.end(),
341 std::back_inserter(CommonArgExpansions));
342}
343
344
345
346
347
348
349
350
351
352
353
354static void
358
360
361 for (const auto &Range : Ranges) {
363 continue;
364
366 bool IsTokenRange = Range.isTokenRange();
367
369 FileID EndFileID = SM->getFileID(End);
370
371
372
373
374 llvm::SmallDenseMap<FileID, SourceLocation> BeginLocsMap;
375 while (Begin.isMacroID() && BeginFileID != EndFileID) {
376 BeginLocsMap[BeginFileID] = Begin;
377 Begin = SM->getImmediateExpansionRange(Begin).getBegin();
378 BeginFileID = SM->getFileID(Begin);
379 }
380
381
382 if (BeginFileID != EndFileID) {
383 while (End.isMacroID() && !BeginLocsMap.count(EndFileID)) {
384 auto Exp = SM->getImmediateExpansionRange(End);
385 IsTokenRange = Exp.isTokenRange();
386 End = Exp.getEnd();
387 EndFileID = SM->getFileID(End);
388 }
389 if (End.isMacroID()) {
390 Begin = BeginLocsMap[EndFileID];
391 BeginFileID = EndFileID;
392 }
393 }
394
395
396
397
398
399 if (Begin.isInvalid() || End.isInvalid() || BeginFileID != EndFileID)
400 continue;
401
402
406 CommonArgExpansions, true, SM,
407 IsTokenRange);
409 CommonArgExpansions, false, SM,
410 IsTokenRange);
412
413
415 End = SM->getSpellingLoc(End);
416
418 IsTokenRange));
419 }
420}
421
429}
430
431
432
433void DiagnosticRenderer::emitSingleMacroExpansion(
436
437
439
440
443
445 llvm::raw_svector_ostream Message(MessageStorage);
448 if (MacroName.empty())
449 Message << "expanded from here";
450 else
451 Message << "expanded from macro '" << MacroName << "'";
452
454 SpellingRanges, {});
455}
456
457
458
459
464 if (SM.isMacroArgExpansion(Loc, &MacroLoc)) {
465 if (ArgumentLoc == MacroLoc) return true;
466 }
467
468 return false;
469}
470
471
472
477 while (BegLoc != EndLoc) {
479 return false;
481 }
482
484}
485
486
487
490 assert(Loc.isMacroID() && "Must be a macro expansion!");
491
494
495
496 unsigned ValidCount =
497 llvm::count_if(Ranges, [](const auto &R) { return R.isValid(); });
498
499 if (ValidCount > SpellingRanges.size())
500 return false;
501
502
504
505
506
507 if (.isMacroArgExpansion(&ArgumentLoc))
508 return false;
509
510 for (const auto &Range : SpellingRanges)
512 return false;
513
514 return true;
515}
516
517
518
519
520
521
522
523
524
525
526
527
528void DiagnosticRenderer::emitMacroExpansions(FullSourceLoc Loc,
532 assert(Loc.isValid() && "must have a valid source location here");
535
536
538 unsigned IgnoredEnd = 0;
540
541
542 if (SM.isMacroArgExpansion(L))
543 LocationStack.push_back(SM.getImmediateExpansionRange(L).getBegin());
544 else
545 LocationStack.push_back(L);
546
548 IgnoredEnd = LocationStack.size();
549
550 L = SM.getImmediateMacroCallerLoc(L);
551
552
553
554
556 L = SM.getImmediateMacroCallerLoc(LocationStack.back());
557 assert(L.isValid() && "must have a valid source location here");
558 }
559
560 LocationStack.erase(LocationStack.begin(),
561 LocationStack.begin() + IgnoredEnd);
562
563 unsigned MacroDepth = LocationStack.size();
564 unsigned MacroLimit = DiagOpts->MacroBacktraceLimit;
565 if (MacroDepth <= MacroLimit || MacroLimit == 0) {
566 for (auto I = LocationStack.rbegin(), E = LocationStack.rend();
567 I != E; ++I)
568 emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges);
569 return;
570 }
571
572 unsigned MacroStartMessages = MacroLimit / 2;
573 unsigned MacroEndMessages = MacroLimit / 2 + MacroLimit % 2;
574
575 for (auto I = LocationStack.rbegin(),
576 E = LocationStack.rbegin() + MacroStartMessages;
577 I != E; ++I)
578 emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges);
579
581 llvm::raw_svector_ostream Message(MessageStorage);
582 Message << "(skipping " << (MacroDepth - MacroLimit)
583 << " expansions in backtrace; use -fmacro-backtrace-limit=0 to "
584 "see all)";
585 emitBasicNote(Message.str());
586
587 for (auto I = LocationStack.rend() - MacroEndMessages,
588 E = LocationStack.rend();
589 I != E; ++I)
590 emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges);
591}
592
594
597
599 llvm::raw_svector_ostream Message(MessageStorage);
600 Message << "in file included from " << PLoc.getFilename() << ':'
601 << PLoc.getLine() << ":";
603}
604
607 StringRef ModuleName) {
608
610 llvm::raw_svector_ostream Message(MessageStorage);
611 Message << "in module '" << ModuleName;
613 Message << "' imported from " << PLoc.getFilename() << ':'
615 Message << ":";
617}
618
621 StringRef ModuleName) {
622
624 llvm::raw_svector_ostream Message(MessageStorage);
626 Message << "while building module '" << ModuleName << "' imported from "
628 else
629 Message << "while building module '" << ModuleName << "':";
631}
Defines the Diagnostic-related interfaces.
static bool checkLocForMacroArgExpansion(SourceLocation Loc, const SourceManager &SM, SourceLocation ArgumentLoc)
Check that the macro argument location of Loc starts with ArgumentLoc.
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.
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.
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.
static bool checkRangeForMacroArgExpansion(CharSourceRange Range, const SourceManager &SM, SourceLocation ArgumentLoc)
Check if all the locations in the range have the same macro argument expansion, and that the expansio...
static bool checkRangesForMacroArgExpansion(FullSourceLoc Loc, ArrayRef< CharSourceRange > Ranges)
A helper function to check if the current ranges are all inside the same macro argument expansion as ...
static void mapDiagnosticRanges(FullSourceLoc CaretLoc, ArrayRef< CharSourceRange > Ranges, SmallVectorImpl< CharSourceRange > &SpellingRanges)
static void mergeFixits(ArrayRef< FixItHint > FixItHints, const SourceManager &SM, const LangOptions &LangOpts, SmallVectorImpl< FixItHint > &MergedFixits)
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
void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) override
void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc, StringRef ModuleName) override
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)
SourceLocation LastLoc
The location of the previous diagnostic if known.
DiagnosticRenderer(const LangOptions &LangOpts, 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.
virtual void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level, SmallVectorImpl< CharSourceRange > &Ranges, ArrayRef< FixItHint > Hints)=0
IntrusiveRefCntPtr< DiagnosticOptions > DiagOpts
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.
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.
SourceLocation getEnd() const
SourceLocation getBegin() const
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.
llvm::PointerUnion< const Diagnostic *, const StoredDiagnostic * > DiagOrStoredDiag