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"
34 : LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {}
45 : MergedFixits(MergedFixits) {}
62 for (
const auto &Hint : FixItHints)
63 if (Hint.CodeToInsert.empty()) {
64 if (Hint.InsertFromRange.isValid())
66 Hint.InsertFromRange,
false,
67 Hint.BeforePreviousInsertions);
69 commit.
remove(Hint.RemoveRange);
71 if (Hint.RemoveRange.isTokenRange() ||
72 Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd())
73 commit.
replace(Hint.RemoveRange, Hint.CodeToInsert);
75 commit.
insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert,
76 false, Hint.BeforePreviousInsertions);
80 if (Editor.
commit(commit)) {
81 FixitReceiver Rec(MergedFixits);
105 if (!FixItHints.empty()) {
107 FixItHints = MergedFixits;
110 for (
const auto &Hint : FixItHints)
111 if (Hint.RemoveRange.isValid())
112 MutableRanges.push_back(Hint.RemoveRange);
123 emitIncludeStack(Loc, PLoc, Level);
127 emitCaret(Loc, Level, MutableRanges, FixItHints);
132 emitMacroExpansions(UnexpandedLoc, Level, MutableRanges, FixItHints);
148void DiagnosticRenderer::emitBasicNote(StringRef Message) {
180 emitIncludeStackRecursively(IncludeLoc);
183 emitImportStack(Loc);
189void DiagnosticRenderer::emitIncludeStackRecursively(
FullSourceLoc Loc) {
203 if (!Imported.second.empty()) {
205 emitImportStackRecursively(Imported.first, Imported.second);
210 emitIncludeStackRecursively(
225 emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second);
230void DiagnosticRenderer::emitImportStackRecursively(
FullSourceLoc Loc,
231 StringRef ModuleName) {
232 if (ModuleName.empty()) {
240 emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second);
248void DiagnosticRenderer::emitModuleBuildStack(
const SourceManager &
SM) {
250 for (
const auto &I : Stack) {
264 bool &IsTokenRange) {
265 assert(
SM->getFileID(Loc) == MacroFileID);
266 if (MacroFileID == CaretFileID)
273 if (
SM->isMacroArgExpansion(Loc)) {
276 if (std::binary_search(CommonArgExpansions.begin(),
277 CommonArgExpansions.end(), MacroFileID))
280 MacroArgRange =
SM->getImmediateExpansionRange(Loc);
282 MacroRange =
SM->getImmediateExpansionRange(Loc);
290 MacroFileID =
SM->getFileID(MacroLocation);
291 bool TokenRange = IsBegin ? IsTokenRange : MacroRange.
isTokenRange();
294 CommonArgExpansions, IsBegin,
SM, TokenRange);
296 IsTokenRange = TokenRange;
297 return MacroLocation;
308 MacroFileID =
SM->getFileID(MacroArgLocation);
310 CommonArgExpansions, IsBegin,
SM, IsTokenRange);
319 if (
SM->isMacroArgExpansion(Loc)) {
320 IDs.push_back(
SM->getFileID(Loc));
321 Loc =
SM->getImmediateSpellingLoc(Loc);
323 auto ExpRange =
SM->getImmediateExpansionRange(Loc);
324 Loc = IsBegin ? ExpRange.getBegin() : ExpRange.getEnd();
338 llvm::sort(BeginArgExpansions);
339 llvm::sort(EndArgExpansions);
340 std::set_intersection(BeginArgExpansions.begin(), BeginArgExpansions.end(),
341 EndArgExpansions.begin(), EndArgExpansions.end(),
342 std::back_inserter(CommonArgExpansions));
362 for (
const auto &Range : Ranges) {
363 if (Range.isInvalid())
367 bool IsTokenRange = Range.isTokenRange();
370 FileID EndFileID =
SM->getFileID(End);
375 llvm::SmallDenseMap<FileID, SourceLocation> BeginLocsMap;
377 BeginLocsMap[BeginFileID] =
Begin;
378 Begin =
SM->getImmediateExpansionRange(
Begin).getBegin();
379 BeginFileID =
SM->getFileID(
Begin);
383 if (BeginFileID != EndFileID) {
384 while (End.isMacroID() && !BeginLocsMap.count(EndFileID)) {
385 auto Exp =
SM->getImmediateExpansionRange(End);
386 IsTokenRange = Exp.isTokenRange();
388 EndFileID =
SM->getFileID(End);
390 if (End.isMacroID()) {
391 Begin = BeginLocsMap[EndFileID];
392 BeginFileID = EndFileID;
400 if (
Begin.
isInvalid() || End.isInvalid() || BeginFileID != EndFileID)
407 CommonArgExpansions,
true,
SM,
410 CommonArgExpansions,
false,
SM,
416 End =
SM->getSpellingLoc(End);
434void DiagnosticRenderer::emitSingleMacroExpansion(
446 llvm::raw_svector_ostream
Message(MessageStorage);
449 if (MacroName.empty())
450 Message <<
"expanded from here";
452 Message <<
"expanded from macro '" << MacroName <<
"'";
455 SpellingRanges, std::nullopt);
465 if (
SM.isMacroArgExpansion(Loc, &MacroLoc)) {
466 if (ArgumentLoc == MacroLoc)
return true;
477 SourceLocation BegLoc = Range.getBegin(), EndLoc = Range.getEnd();
478 while (BegLoc != EndLoc) {
491 assert(Loc.
isMacroID() &&
"Must be a macro expansion!");
497 unsigned ValidCount =
498 llvm::count_if(Ranges, [](
const auto &R) {
return R.isValid(); });
500 if (ValidCount > SpellingRanges.size())
511 for (
const auto &Range : SpellingRanges)
529void DiagnosticRenderer::emitMacroExpansions(
FullSourceLoc Loc,
533 assert(Loc.
isValid() &&
"must have a valid source location here");
539 unsigned IgnoredEnd = 0;
543 if (
SM.isMacroArgExpansion(L))
544 LocationStack.push_back(
SM.getImmediateExpansionRange(L).getBegin());
546 LocationStack.push_back(L);
549 IgnoredEnd = LocationStack.size();
551 L =
SM.getImmediateMacroCallerLoc(L);
557 L =
SM.getImmediateMacroCallerLoc(LocationStack.back());
558 assert(L.
isValid() &&
"must have a valid source location here");
561 LocationStack.erase(LocationStack.begin(),
562 LocationStack.begin() + IgnoredEnd);
564 unsigned MacroDepth = LocationStack.size();
565 unsigned MacroLimit =
DiagOpts->MacroBacktraceLimit;
566 if (MacroDepth <= MacroLimit || MacroLimit == 0) {
567 for (
auto I = LocationStack.rbegin(), E = LocationStack.rend();
573 unsigned MacroStartMessages = MacroLimit / 2;
574 unsigned MacroEndMessages = MacroLimit / 2 + MacroLimit % 2;
576 for (
auto I = LocationStack.rbegin(),
577 E = LocationStack.rbegin() + MacroStartMessages;
582 llvm::raw_svector_ostream
Message(MessageStorage);
583 Message <<
"(skipping " << (MacroDepth - MacroLimit)
584 <<
" expansions in backtrace; use -fmacro-backtrace-limit=0 to "
588 for (
auto I = LocationStack.rend() - MacroEndMessages,
589 E = LocationStack.rend();
600 llvm::raw_svector_ostream Message(MessageStorage);
601 Message <<
"in file included from " << PLoc.
getFilename() <<
':'
608 StringRef ModuleName) {
611 llvm::raw_svector_ostream Message(MessageStorage);
612 Message <<
"in module '" << ModuleName;
614 Message <<
"' imported from " << PLoc.
getFilename() <<
':'
622 StringRef ModuleName) {
625 llvm::raw_svector_ostream Message(MessageStorage);
627 Message <<
"while building module '" << ModuleName <<
"' imported from "
630 Message <<
"while building module '" << ModuleName <<
"':";
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.
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.
bool isMacroArgExpansion(FullSourceLoc *StartLoc=nullptr) const
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)
llvm::PointerUnion< const Diagnostic *, const StoredDiagnostic * > DiagOrStoredDiag