#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/DiagnosticError.h"
#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Basic/TokenKinds.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/Unicode.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>
#include <string>
#include <utility>
#include <vector>
using namespace clang;
const StreamingDiagnostic &clang::operator<<(const StreamingDiagnostic &DB,
DiagNullabilityKind nullability) {
StringRef string;
switch (nullability.first) {
case NullabilityKind::NonNull:
string = nullability.second ? "'nonnull'" : "'_Nonnull'";
break;
case NullabilityKind::Nullable:
string = nullability.second ? "'nullable'" : "'_Nullable'";
break;
case NullabilityKind::Unspecified:
string = nullability.second ? "'null_unspecified'" : "'_Null_unspecified'";
break;
case NullabilityKind::NullableResult:
assert(!nullability.second &&
"_Nullable_result isn't supported as context-sensitive keyword");
string = "_Nullable_result";
break;
}
DB.AddString(string);
return DB;
}
const StreamingDiagnostic &clang::operator<<(const StreamingDiagnostic &DB,
llvm::Error &&E) {
DB.AddString(toString(std::move(E)));
return DB;
}
static void DummyArgToStringFn(DiagnosticsEngine::ArgumentKind AK, intptr_t QT,
StringRef Modifier, StringRef Argument,
ArrayRef<DiagnosticsEngine::ArgumentValue> PrevArgs,
SmallVectorImpl<char> &Output,
void *Cookie,
ArrayRef<intptr_t> QualTypeVals) {
StringRef Str = "<can't format argument>";
Output.append(Str.begin(), Str.end());
}
DiagnosticsEngine::DiagnosticsEngine(
IntrusiveRefCntPtr<DiagnosticIDs> diags,
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts, DiagnosticConsumer *client,
bool ShouldOwnClient)
: Diags(std::move(diags)), DiagOpts(std::move(DiagOpts)) {
setClient(client, ShouldOwnClient);
ArgToStringFn = DummyArgToStringFn;
Reset();
}
DiagnosticsEngine::~DiagnosticsEngine() {
setClient(nullptr);
}
void DiagnosticsEngine::dump() const {
DiagStatesByLoc.dump(*SourceMgr);
}
void DiagnosticsEngine::dump(StringRef DiagName) const {
DiagStatesByLoc.dump(*SourceMgr, DiagName);
}
void DiagnosticsEngine::setClient(DiagnosticConsumer *client,
bool ShouldOwnClient) {
Owner.reset(ShouldOwnClient ? client : nullptr);
Client = client;
}
void DiagnosticsEngine::pushMappings(SourceLocation Loc) {
DiagStateOnPushStack.push_back(GetCurDiagState());
}
bool DiagnosticsEngine::popMappings(SourceLocation Loc) {
if (DiagStateOnPushStack.empty())
return false;
if (DiagStateOnPushStack.back() != GetCurDiagState()) {
PushDiagStatePoint(DiagStateOnPushStack.back(), Loc);
}
DiagStateOnPushStack.pop_back();
return true;
}
void DiagnosticsEngine::Reset(bool soft ) {
ErrorOccurred = false;
UncompilableErrorOccurred = false;
FatalErrorOccurred = false;
UnrecoverableErrorOccurred = false;
NumWarnings = 0;
NumErrors = 0;
TrapNumErrorsOccurred = 0;
TrapNumUnrecoverableErrorsOccurred = 0;
CurDiagID = std::numeric_limits<unsigned>::max();
LastDiagLevel = DiagnosticIDs::Ignored;
DelayedDiagID = 0;
if (!soft) {
DiagStates.clear();
DiagStatesByLoc.clear();
DiagStateOnPushStack.clear();
DiagStates.emplace_back();
DiagStatesByLoc.appendFirst(&DiagStates.back());
}
}
void DiagnosticsEngine::SetDelayedDiagnostic(unsigned DiagID, StringRef Arg1,
StringRef Arg2, StringRef Arg3) {
if (DelayedDiagID)
return;
DelayedDiagID = DiagID;
DelayedDiagArg1 = Arg1.str();
DelayedDiagArg2 = Arg2.str();
DelayedDiagArg3 = Arg3.str();
}
void DiagnosticsEngine::ReportDelayed() {
unsigned ID = DelayedDiagID;
DelayedDiagID = 0;
Report(ID) << DelayedDiagArg1 << DelayedDiagArg2 << DelayedDiagArg3;
}
void DiagnosticsEngine::DiagStateMap::appendFirst(DiagState *State) {
assert(Files.empty() && "not first");
FirstDiagState = CurDiagState = State;
CurDiagStateLoc = SourceLocation();
}
void DiagnosticsEngine::DiagStateMap::append(SourceManager &SrcMgr,
SourceLocation Loc,
DiagState *State) {
CurDiagState = State;
CurDiagStateLoc = Loc;
std::pair<FileID, unsigned> Decomp = SrcMgr.getDecomposedLoc(Loc);
unsigned Offset = Decomp.second;
for (File *F = getFile(SrcMgr, Decomp.first); F;
Offset = F->ParentOffset, F = F->Parent) {
F->HasLocalTransitions = true;
auto &Last = F->StateTransitions.back();
assert(Last.Offset <= Offset && "state transitions added out of order");
if (Last.Offset == Offset) {
if (Last.State == State)
break;
Last.State = State;
continue;
}
F->StateTransitions.push_back({State, Offset});
}
}
DiagnosticsEngine::DiagState *
DiagnosticsEngine::DiagStateMap::lookup(SourceManager &SrcMgr,
SourceLocation Loc) const {
if (Files.empty())
return FirstDiagState;
std::pair<FileID, unsigned> Decomp = SrcMgr.getDecomposedLoc(Loc);
const File *F = getFile(SrcMgr, Decomp.first);
return F->lookup(Decomp.second);
}
DiagnosticsEngine::DiagState *
DiagnosticsEngine::DiagStateMap::File::lookup(unsigned Offset) const {
auto OnePastIt =
llvm::partition_point(StateTransitions, [=](const DiagStatePoint &P) {
return P.Offset <= Offset;
});
assert(OnePastIt != StateTransitions.begin() && "missing initial state");
return OnePastIt[-1].State;
}
DiagnosticsEngine::DiagStateMap::File *
DiagnosticsEngine::DiagStateMap::getFile(SourceManager &SrcMgr,
FileID ID) const {
auto Range = Files.equal_range(ID);
if (Range.first != Range.second)
return &Range.first->second;
auto &F = Files.insert(Range.first, std::make_pair(ID, File()))->second;
if (ID.isValid()) {
std::pair<FileID, unsigned> Decomp = SrcMgr.getDecomposedIncludedLoc(ID);
F.Parent = getFile(SrcMgr, Decomp.first);
F.ParentOffset = Decomp.second;
F.StateTransitions.push_back({F.Parent->lookup(Decomp.second), 0});
} else {
F.StateTransitions.push_back({FirstDiagState, 0});
}
return &F;
}
void DiagnosticsEngine::DiagStateMap::dump(SourceManager &SrcMgr,
StringRef DiagName) const {
llvm::errs() << "diagnostic state at ";
CurDiagStateLoc.print(llvm::errs(), SrcMgr);
llvm::errs() << ": " << CurDiagState << "\n";
for (auto &F : Files) {
FileID ID = F.first;
File &File = F.second;
bool PrintedOuterHeading = false;
auto PrintOuterHeading = [&] {
if (PrintedOuterHeading) return;
PrintedOuterHeading = true;
llvm::errs() << "File " << &File << " <FileID " << ID.getHashValue()
<< ">: " << SrcMgr.getBufferOrFake(ID).getBufferIdentifier();
if (F.second.Parent) {
std::pair<FileID, unsigned> Decomp =
SrcMgr.getDecomposedIncludedLoc(ID);
assert(File.ParentOffset == Decomp.second);
llvm::errs() << " parent " << File.Parent << " <FileID "
<< Decomp.first.getHashValue() << "> ";
SrcMgr.getLocForStartOfFile(Decomp.first)
.getLocWithOffset(Decomp.second)
.print(llvm::errs(), SrcMgr);
}
if (File.HasLocalTransitions)
llvm::errs() << " has_local_transitions";
llvm::errs() << "\n";
};
if (DiagName.empty())
PrintOuterHeading();
for (DiagStatePoint &Transition : File.StateTransitions) {
bool PrintedInnerHeading = false;
auto PrintInnerHeading = [&] {
if (PrintedInnerHeading) return;
PrintedInnerHeading = true;
PrintOuterHeading();
llvm::errs() << " ";
SrcMgr.getLocForStartOfFile(ID)
.getLocWithOffset(Transition.Offset)
.print(llvm::errs(), SrcMgr);
llvm::errs() << ": state " << Transition.State << ":\n";
};
if (DiagName.empty())
PrintInnerHeading();
for (auto &Mapping : *Transition.State) {
StringRef Option =
DiagnosticIDs::getWarningOptionForDiag(Mapping.first);
if (!DiagName.empty() && DiagName != Option)
continue;
PrintInnerHeading();
llvm::errs() << " ";
if (Option.empty())
llvm::errs() << "<unknown " << Mapping.first << ">";
else
llvm::errs() << Option;
llvm::errs() << ": ";
switch (Mapping.second.getSeverity()) {
case diag::Severity::Ignored: llvm::errs() << "ignored"; break;
case diag::Severity::Remark: llvm::errs() << "remark"; break;
case diag::Severity::Warning: llvm::errs() << "warning"; break;
case diag::Severity::Error: llvm::errs() << "error"; break;
case diag::Severity::Fatal: llvm::errs() << "fatal"; break;
}
if (!Mapping.second.isUser())
llvm::errs() << " default";
if (Mapping.second.isPragma())
llvm::errs() << " pragma";
if (Mapping.second.hasNoWarningAsError())
llvm::errs() << " no-error";
if (Mapping.second.hasNoErrorAsFatal())
llvm::errs() << " no-fatal";
if (Mapping.second.wasUpgradedFromWarning())
llvm::errs() << " overruled";
llvm::errs() << "\n";
}
}
}
}
void DiagnosticsEngine::PushDiagStatePoint(DiagState *State,
SourceLocation Loc) {
assert(Loc.isValid() && "Adding invalid loc point");
DiagStatesByLoc.append(*SourceMgr, Loc, State);
}
void DiagnosticsEngine::setSeverity(diag::kind Diag, diag::Severity Map,
SourceLocation L) {
assert(Diag < diag::DIAG_UPPER_LIMIT &&
"Can only map builtin diagnostics");
assert((Diags->isBuiltinWarningOrExtension(Diag) ||
(Map == diag::Severity::Fatal || Map == diag::Severity::Error)) &&
"Cannot map errors into warnings!");
assert((L.isInvalid() || SourceMgr) && "No SourceMgr for valid location");
bool WasUpgradedFromWarning = false;
if (Map == diag::Severity::Warning) {
DiagnosticMapping &Info = GetCurDiagState()->getOrAddMapping(Diag);
if (Info.getSeverity() == diag::Severity::Error ||
Info.getSeverity() == diag::Severity::Fatal) {
Map = Info.getSeverity();
WasUpgradedFromWarning = true;
}
}
DiagnosticMapping Mapping = makeUserMapping(Map, L);
Mapping.setUpgradedFromWarning(WasUpgradedFromWarning);
DiagnosticMapping &Info = GetCurDiagState()->getOrAddMapping(Diag);
Mapping.setNoWarningAsError(Info.hasNoWarningAsError() ||
Mapping.hasNoWarningAsError());
if ((L.isInvalid() || L == DiagStatesByLoc.getCurDiagStateLoc()) &&
DiagStatesByLoc.getCurDiagState()) {
DiagStatesByLoc.getCurDiagState()->setMapping(Diag, Mapping);
return;
}
DiagStates.push_back(*GetCurDiagState());
DiagStates.back().setMapping(Diag, Mapping);
PushDiagStatePoint(&DiagStates.back(), L);
}
bool DiagnosticsEngine::setSeverityForGroup(diag::Flavor Flavor,
StringRef Group, diag::Severity Map,
SourceLocation Loc) {
SmallVector<diag::kind, 256> GroupDiags;
if (Diags->getDiagnosticsInGroup(Flavor, Group, GroupDiags))
return true;
for (diag::kind Diag : GroupDiags)
setSeverity(Diag, Map, Loc);
return false;
}
bool DiagnosticsEngine::setSeverityForGroup(diag::Flavor Flavor,
diag::Group Group,
diag::Severity Map,
SourceLocation Loc) {
return setSeverityForGroup(Flavor, Diags->getWarningOptionForGroup(Group),
Map, Loc);
}
bool DiagnosticsEngine::setDiagnosticGroupWarningAsError(StringRef Group,
bool Enabled) {
if (Enabled)
return setSeverityForGroup(diag::Flavor::WarningOrError, Group,
diag::Severity::Error);
SmallVector<diag::kind, 8> GroupDiags;
if (Diags->getDiagnosticsInGroup(diag::Flavor::WarningOrError, Group,
GroupDiags))
return true;
for (diag::kind Diag : GroupDiags) {
DiagnosticMapping &Info = GetCurDiagState()->getOrAddMapping(Diag);
if (Info.getSeverity() == diag::Severity::Error ||
Info.getSeverity() == diag::Severity::Fatal)
Info.setSeverity(diag::Severity::Warning);
Info.setNoWarningAsError(true);
}
return false;
}
bool DiagnosticsEngine::setDiagnosticGroupErrorAsFatal(StringRef Group,
bool Enabled) {
if (Enabled)
return setSeverityForGroup(diag::Flavor::WarningOrError, Group,
diag::Severity::Fatal);
SmallVector<diag::kind, 8> GroupDiags;
if (Diags->getDiagnosticsInGroup(diag::Flavor::WarningOrError, Group,
GroupDiags))
return true;
for (diag::kind Diag : GroupDiags) {
DiagnosticMapping &Info = GetCurDiagState()->getOrAddMapping(Diag);
if (Info.getSeverity() == diag::Severity::Fatal)
Info.setSeverity(diag::Severity::Error);
Info.setNoErrorAsFatal(true);
}
return false;
}
void DiagnosticsEngine::setSeverityForAll(diag::Flavor Flavor,
diag::Severity Map,
SourceLocation Loc) {
std::vector<diag::kind> AllDiags;
DiagnosticIDs::getAllDiagnostics(Flavor, AllDiags);
for (diag::kind Diag : AllDiags)
if (Diags->isBuiltinWarningOrExtension(Diag))
setSeverity(Diag, Map, Loc);
}
void DiagnosticsEngine::Report(const StoredDiagnostic &storedDiag) {
assert(CurDiagID == std::numeric_limits<unsigned>::max() &&
"Multiple diagnostics in flight at once!");
CurDiagLoc = storedDiag.getLocation();
CurDiagID = storedDiag.getID();
DiagStorage.NumDiagArgs = 0;
DiagStorage.DiagRanges.clear();
DiagStorage.DiagRanges.append(storedDiag.range_begin(),
storedDiag.range_end());
DiagStorage.FixItHints.clear();
DiagStorage.FixItHints.append(storedDiag.fixit_begin(),
storedDiag.fixit_end());
assert(Client && "DiagnosticConsumer not set!");
Level DiagLevel = storedDiag.getLevel();
Diagnostic Info(this, storedDiag.getMessage());
Client->HandleDiagnostic(DiagLevel, Info);
if (Client->IncludeInDiagnosticCounts()) {
if (DiagLevel == DiagnosticsEngine::Warning)
++NumWarnings;
}
CurDiagID = std::numeric_limits<unsigned>::max();
}
bool DiagnosticsEngine::EmitCurrentDiagnostic(bool Force) {
assert(getClient() && "DiagnosticClient not set!");
bool Emitted;
if (Force) {
Diagnostic Info(this);
DiagnosticIDs::Level DiagLevel
= Diags->getDiagnosticLevel(Info.getID(), Info.getLocation(), *this);
Emitted = (DiagLevel != DiagnosticIDs::Ignored);
if (Emitted) {
Diags->EmitDiag(*this, DiagLevel);
}
} else {
Emitted = ProcessDiag();
}
Clear();
if (!Force && DelayedDiagID)
ReportDelayed();
return Emitted;
}
DiagnosticConsumer::~DiagnosticConsumer() = default;
void DiagnosticConsumer::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Info) {
if (!IncludeInDiagnosticCounts())
return;
if (DiagLevel == DiagnosticsEngine::Warning)
++NumWarnings;
else if (DiagLevel >= DiagnosticsEngine::Error)
++NumErrors;
}
template <std::size_t StrLen>
static bool ModifierIs(const char *Modifier, unsigned ModifierLen,
const char (&Str)[StrLen]) {
return StrLen-1 == ModifierLen && memcmp(Modifier, Str, StrLen-1) == 0;
}
static const char *ScanFormat(const char *I, const char *E, char Target) {
unsigned Depth = 0;
for ( ; I != E; ++I) {
if (Depth == 0 && *I == Target) return I;
if (Depth != 0 && *I == '}') Depth--;
if (*I == '%') {
I++;
if (I == E) break;
if (!isDigit(*I) && !isPunctuation(*I)) {
for (I++; I != E && !isDigit(*I) && *I != '{'; I++) ;
if (I == E) break;
if (*I == '{')
Depth++;
}
}
}
return E;
}
static void HandleSelectModifier(const Diagnostic &DInfo, unsigned ValNo,
const char *Argument, unsigned ArgumentLen,
SmallVectorImpl<char> &OutStr) {
const char *ArgumentEnd = Argument+ArgumentLen;
while (ValNo) {
const char *NextVal = ScanFormat(Argument, ArgumentEnd, '|');
assert(NextVal != ArgumentEnd && "Value for integer select modifier was"
" larger than the number of options in the diagnostic string!");
Argument = NextVal+1; --ValNo;
}
const char *EndPtr = ScanFormat(Argument, ArgumentEnd, '|');
DInfo.FormatDiagnostic(Argument, EndPtr, OutStr);
}
static void HandleIntegerSModifier(unsigned ValNo,
SmallVectorImpl<char> &OutStr) {
if (ValNo != 1)
OutStr.push_back('s');
}
static void HandleOrdinalModifier(unsigned ValNo,
SmallVectorImpl<char> &OutStr) {
assert(ValNo != 0 && "ValNo must be strictly positive!");
llvm::raw_svector_ostream Out(OutStr);
Out << ValNo << llvm::getOrdinalSuffix(ValNo);
}
static unsigned PluralNumber(const char *&Start, const char *End) {
unsigned Val = 0;
while (Start != End && *Start >= '0' && *Start <= '9') {
Val *= 10;
Val += *Start - '0';
++Start;
}
return Val;
}
static bool TestPluralRange(unsigned Val, const char *&Start, const char *End) {
if (*Start != '[') {
unsigned Ref = PluralNumber(Start, End);
return Ref == Val;
}
++Start;
unsigned Low = PluralNumber(Start, End);
assert(*Start == ',' && "Bad plural expression syntax: expected ,");
++Start;
unsigned High = PluralNumber(Start, End);
assert(*Start == ']' && "Bad plural expression syntax: expected )");
++Start;
return Low <= Val && Val <= High;
}
static bool EvalPluralExpr(unsigned ValNo, const char *Start, const char *End) {
if (*Start == ':')
return true;
while (true) {
char C = *Start;
if (C == '%') {
++Start;
unsigned Arg = PluralNumber(Start, End);
assert(*Start == '=' && "Bad plural expression syntax: expected =");
++Start;
unsigned ValMod = ValNo % Arg;
if (TestPluralRange(ValMod, Start, End))
return true;
} else {
assert((C == '[' || (C >= '0' && C <= '9')) &&
"Bad plural expression syntax: unexpected character");
if (TestPluralRange(ValNo, Start, End))
return true;
}
Start = std::find(Start, End, ',');
if (Start == End)
break;
++Start;
}
return false;
}
static void HandlePluralModifier(const Diagnostic &DInfo, unsigned ValNo,
const char *Argument, unsigned ArgumentLen,
SmallVectorImpl<char> &OutStr) {
const char *ArgumentEnd = Argument + ArgumentLen;
while (true) {
assert(Argument < ArgumentEnd && "Plural expression didn't match.");
const char *ExprEnd = Argument;
while (*ExprEnd != ':') {
assert(ExprEnd != ArgumentEnd && "Plural missing expression end");
++ExprEnd;
}
if (EvalPluralExpr(ValNo, Argument, ExprEnd)) {
Argument = ExprEnd + 1;
ExprEnd = ScanFormat(Argument, ArgumentEnd, '|');
DInfo.FormatDiagnostic(Argument, ExprEnd, OutStr);
return;
}
Argument = ScanFormat(Argument, ArgumentEnd - 1, '|') + 1;
}
}
static const char *getTokenDescForDiagnostic(tok::TokenKind Kind) {
switch (Kind) {
case tok::identifier:
return "identifier";
default:
return nullptr;
}
}
void Diagnostic::
FormatDiagnostic(SmallVectorImpl<char> &OutStr) const {
if (!StoredDiagMessage.empty()) {
OutStr.append(StoredDiagMessage.begin(), StoredDiagMessage.end());
return;
}
StringRef Diag =
getDiags()->getDiagnosticIDs()->getDescription(getID());
FormatDiagnostic(Diag.begin(), Diag.end(), OutStr);
}
static void pushEscapedString(StringRef Str, SmallVectorImpl<char> &OutStr) {
OutStr.reserve(OutStr.size() + Str.size());
auto *Begin = reinterpret_cast<const unsigned char *>(Str.data());
llvm::raw_svector_ostream OutStream(OutStr);
const unsigned char *End = Begin + Str.size();
while (Begin != End) {
if (isPrintable(*Begin) || isWhitespace(*Begin)) {
OutStream << *Begin;
++Begin;
continue;
}
if (llvm::isLegalUTF8Sequence(Begin, End)) {
llvm::UTF32 CodepointValue;
llvm::UTF32 *CpPtr = &CodepointValue;
const unsigned char *CodepointBegin = Begin;
const unsigned char *CodepointEnd =
Begin + llvm::getNumBytesForUTF8(*Begin);
llvm::ConversionResult Res = llvm::ConvertUTF8toUTF32(
&Begin, CodepointEnd, &CpPtr, CpPtr + 1, llvm::strictConversion);
(void)Res;
assert(
llvm::conversionOK == Res &&
"the sequence is legal UTF-8 but we couldn't convert it to UTF-32");
assert(Begin == CodepointEnd &&
"we must be further along in the string now");
if (llvm::sys::unicode::isPrintable(CodepointValue) ||
llvm::sys::unicode::isFormatting(CodepointValue)) {
OutStr.append(CodepointBegin, CodepointEnd);
continue;
}
OutStream << "<U+" << llvm::format_hex_no_prefix(CodepointValue, 4, true)
<< ">";
continue;
}
OutStream << "<" << llvm::format_hex_no_prefix(*Begin, 2, true) << ">";
++Begin;
}
}
void Diagnostic::
FormatDiagnostic(const char *DiagStr, const char *DiagEnd,
SmallVectorImpl<char> &OutStr) const {
if (DiagEnd - DiagStr == 2 &&
StringRef(DiagStr, DiagEnd - DiagStr).equals("%0") &&
getArgKind(0) == DiagnosticsEngine::ak_std_string) {
const std::string &S = getArgStdStr(0);
pushEscapedString(S, OutStr);
return;
}
SmallVector<DiagnosticsEngine::ArgumentValue, 8> FormattedArgs;
SmallVector<intptr_t, 2> QualTypeVals;
SmallString<64> Tree;
for (unsigned i = 0, e = getNumArgs(); i < e; ++i)
if (getArgKind(i) == DiagnosticsEngine::ak_qualtype)
QualTypeVals.push_back(getRawArg(i));
while (DiagStr != DiagEnd) {
if (DiagStr[0] != '%') {
const char *StrEnd = std::find(DiagStr, DiagEnd, '%');
OutStr.append(DiagStr, StrEnd);
DiagStr = StrEnd;
continue;
} else if (isPunctuation(DiagStr[1])) {
OutStr.push_back(DiagStr[1]); DiagStr += 2;
continue;
}
++DiagStr;
const char *Modifier = nullptr, *Argument = nullptr;
unsigned ModifierLen = 0, ArgumentLen = 0;
if (!isDigit(DiagStr[0])) {
Modifier = DiagStr;
while (DiagStr[0] == '-' ||
(DiagStr[0] >= 'a' && DiagStr[0] <= 'z'))
++DiagStr;
ModifierLen = DiagStr-Modifier;
if (DiagStr[0] == '{') {
++DiagStr; Argument = DiagStr;
DiagStr = ScanFormat(DiagStr, DiagEnd, '}');
assert(DiagStr != DiagEnd && "Mismatched {}'s in diagnostic string!");
ArgumentLen = DiagStr-Argument;
++DiagStr; }
}
assert(isDigit(*DiagStr) && "Invalid format for argument in diagnostic");
unsigned ArgNo = *DiagStr++ - '0';
unsigned ArgNo2 = ArgNo;
DiagnosticsEngine::ArgumentKind Kind = getArgKind(ArgNo);
if (ModifierIs(Modifier, ModifierLen, "diff")) {
assert(*DiagStr == ',' && isDigit(*(DiagStr + 1)) &&
"Invalid format for diff modifier");
++DiagStr; ArgNo2 = *DiagStr++ - '0';
DiagnosticsEngine::ArgumentKind Kind2 = getArgKind(ArgNo2);
if (Kind == DiagnosticsEngine::ak_qualtype &&
Kind2 == DiagnosticsEngine::ak_qualtype)
Kind = DiagnosticsEngine::ak_qualtype_pair;
else {
const char *ArgumentEnd = Argument + ArgumentLen;
const char *Pipe = ScanFormat(Argument, ArgumentEnd, '|');
assert(ScanFormat(Pipe + 1, ArgumentEnd, '|') == ArgumentEnd &&
"Found too many '|'s in a %diff modifier!");
const char *FirstDollar = ScanFormat(Argument, Pipe, '$');
const char *SecondDollar = ScanFormat(FirstDollar + 1, Pipe, '$');
const char ArgStr1[] = { '%', static_cast<char>('0' + ArgNo) };
const char ArgStr2[] = { '%', static_cast<char>('0' + ArgNo2) };
FormatDiagnostic(Argument, FirstDollar, OutStr);
FormatDiagnostic(ArgStr1, ArgStr1 + 2, OutStr);
FormatDiagnostic(FirstDollar + 1, SecondDollar, OutStr);
FormatDiagnostic(ArgStr2, ArgStr2 + 2, OutStr);
FormatDiagnostic(SecondDollar + 1, Pipe, OutStr);
continue;
}
}
switch (Kind) {
case DiagnosticsEngine::ak_std_string: {
const std::string &S = getArgStdStr(ArgNo);
assert(ModifierLen == 0 && "No modifiers for strings yet");
pushEscapedString(S, OutStr);
break;
}
case DiagnosticsEngine::ak_c_string: {
const char *S = getArgCStr(ArgNo);
assert(ModifierLen == 0 && "No modifiers for strings yet");
if (!S)
S = "(null)";
pushEscapedString(S, OutStr);
break;
}
case DiagnosticsEngine::ak_sint: {
int64_t Val = getArgSInt(ArgNo);
if (ModifierIs(Modifier, ModifierLen, "select")) {
HandleSelectModifier(*this, (unsigned)Val, Argument, ArgumentLen,
OutStr);
} else if (ModifierIs(Modifier, ModifierLen, "s")) {
HandleIntegerSModifier(Val, OutStr);
} else if (ModifierIs(Modifier, ModifierLen, "plural")) {
HandlePluralModifier(*this, (unsigned)Val, Argument, ArgumentLen,
OutStr);
} else if (ModifierIs(Modifier, ModifierLen, "ordinal")) {
HandleOrdinalModifier((unsigned)Val, OutStr);
} else {
assert(ModifierLen == 0 && "Unknown integer modifier");
llvm::raw_svector_ostream(OutStr) << Val;
}
break;
}
case DiagnosticsEngine::ak_uint: {
uint64_t Val = getArgUInt(ArgNo);
if (ModifierIs(Modifier, ModifierLen, "select")) {
HandleSelectModifier(*this, Val, Argument, ArgumentLen, OutStr);
} else if (ModifierIs(Modifier, ModifierLen, "s")) {
HandleIntegerSModifier(Val, OutStr);
} else if (ModifierIs(Modifier, ModifierLen, "plural")) {
HandlePluralModifier(*this, (unsigned)Val, Argument, ArgumentLen,
OutStr);
} else if (ModifierIs(Modifier, ModifierLen, "ordinal")) {
HandleOrdinalModifier(Val, OutStr);
} else {
assert(ModifierLen == 0 && "Unknown integer modifier");
llvm::raw_svector_ostream(OutStr) << Val;
}
break;
}
case DiagnosticsEngine::ak_tokenkind: {
tok::TokenKind Kind = static_cast<tok::TokenKind>(getRawArg(ArgNo));
assert(ModifierLen == 0 && "No modifiers for token kinds yet");
llvm::raw_svector_ostream Out(OutStr);
if (const char *S = tok::getPunctuatorSpelling(Kind))
Out << '\'' << S << '\'';
else if ((S = tok::getKeywordSpelling(Kind)))
Out << S;
else if ((S = getTokenDescForDiagnostic(Kind)))
Out << S;
else if ((S = tok::getTokenName(Kind)))
Out << '<' << S << '>';
else
Out << "(null)";
break;
}
case DiagnosticsEngine::ak_identifierinfo: {
const IdentifierInfo *II = getArgIdentifier(ArgNo);
assert(ModifierLen == 0 && "No modifiers for strings yet");
if (!II) {
const char *S = "(null)";
OutStr.append(S, S + strlen(S));
continue;
}
llvm::raw_svector_ostream(OutStr) << '\'' << II->getName() << '\'';
break;
}
case DiagnosticsEngine::ak_addrspace:
case DiagnosticsEngine::ak_qual:
case DiagnosticsEngine::ak_qualtype:
case DiagnosticsEngine::ak_declarationname:
case DiagnosticsEngine::ak_nameddecl:
case DiagnosticsEngine::ak_nestednamespec:
case DiagnosticsEngine::ak_declcontext:
case DiagnosticsEngine::ak_attr:
getDiags()->ConvertArgToString(Kind, getRawArg(ArgNo),
StringRef(Modifier, ModifierLen),
StringRef(Argument, ArgumentLen),
FormattedArgs,
OutStr, QualTypeVals);
break;
case DiagnosticsEngine::ak_qualtype_pair: {
TemplateDiffTypes TDT;
TDT.FromType = getRawArg(ArgNo);
TDT.ToType = getRawArg(ArgNo2);
TDT.ElideType = getDiags()->ElideType;
TDT.ShowColors = getDiags()->ShowColors;
TDT.TemplateDiffUsed = false;
intptr_t val = reinterpret_cast<intptr_t>(&TDT);
const char *ArgumentEnd = Argument + ArgumentLen;
const char *Pipe = ScanFormat(Argument, ArgumentEnd, '|');
if (getDiags()->PrintTemplateTree && Tree.empty()) {
TDT.PrintFromType = true;
TDT.PrintTree = true;
getDiags()->ConvertArgToString(Kind, val,
StringRef(Modifier, ModifierLen),
StringRef(Argument, ArgumentLen),
FormattedArgs,
Tree, QualTypeVals);
if (!Tree.empty()) {
FormatDiagnostic(Pipe + 1, ArgumentEnd, OutStr);
break;
}
}
const char *FirstDollar = ScanFormat(Argument, ArgumentEnd, '$');
const char *SecondDollar = ScanFormat(FirstDollar + 1, ArgumentEnd, '$');
FormatDiagnostic(Argument, FirstDollar, OutStr);
TDT.PrintTree = false;
TDT.PrintFromType = true;
getDiags()->ConvertArgToString(Kind, val,
StringRef(Modifier, ModifierLen),
StringRef(Argument, ArgumentLen),
FormattedArgs,
OutStr, QualTypeVals);
if (!TDT.TemplateDiffUsed)
FormattedArgs.push_back(std::make_pair(DiagnosticsEngine::ak_qualtype,
TDT.FromType));
FormatDiagnostic(FirstDollar + 1, SecondDollar, OutStr);
TDT.PrintFromType = false;
getDiags()->ConvertArgToString(Kind, val,
StringRef(Modifier, ModifierLen),
StringRef(Argument, ArgumentLen),
FormattedArgs,
OutStr, QualTypeVals);
if (!TDT.TemplateDiffUsed)
FormattedArgs.push_back(std::make_pair(DiagnosticsEngine::ak_qualtype,
TDT.ToType));
FormatDiagnostic(SecondDollar + 1, Pipe, OutStr);
break;
}
}
if (Kind == DiagnosticsEngine::ak_qualtype_pair)
continue;
else if (Kind != DiagnosticsEngine::ak_std_string)
FormattedArgs.push_back(std::make_pair(Kind, getRawArg(ArgNo)));
else
FormattedArgs.push_back(std::make_pair(DiagnosticsEngine::ak_c_string,
(intptr_t)getArgStdStr(ArgNo).c_str()));
}
OutStr.append(Tree.begin(), Tree.end());
}
StoredDiagnostic::StoredDiagnostic(DiagnosticsEngine::Level Level, unsigned ID,
StringRef Message)
: ID(ID), Level(Level), Message(Message) {}
StoredDiagnostic::StoredDiagnostic(DiagnosticsEngine::Level Level,
const Diagnostic &Info)
: ID(Info.getID()), Level(Level) {
assert((Info.getLocation().isInvalid() || Info.hasSourceManager()) &&
"Valid source location without setting a source manager for diagnostic");
if (Info.getLocation().isValid())
Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager());
SmallString<64> Message;
Info.FormatDiagnostic(Message);
this->Message.assign(Message.begin(), Message.end());
this->Ranges.assign(Info.getRanges().begin(), Info.getRanges().end());
this->FixIts.assign(Info.getFixItHints().begin(), Info.getFixItHints().end());
}
StoredDiagnostic::StoredDiagnostic(DiagnosticsEngine::Level Level, unsigned ID,
StringRef Message, FullSourceLoc Loc,
ArrayRef<CharSourceRange> Ranges,
ArrayRef<FixItHint> FixIts)
: ID(ID), Level(Level), Loc(Loc), Message(Message),
Ranges(Ranges.begin(), Ranges.end()), FixIts(FixIts.begin(), FixIts.end())
{
}
llvm::raw_ostream &clang::operator<<(llvm::raw_ostream &OS,
const StoredDiagnostic &SD) {
if (SD.getLocation().hasManager())
OS << SD.getLocation().printToString(SD.getLocation().getManager()) << ": ";
OS << SD.getMessage();
return OS;
}
bool DiagnosticConsumer::IncludeInDiagnosticCounts() const { return true; }
void IgnoringDiagConsumer::anchor() {}
ForwardingDiagnosticConsumer::~ForwardingDiagnosticConsumer() = default;
void ForwardingDiagnosticConsumer::HandleDiagnostic(
DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Info) {
Target.HandleDiagnostic(DiagLevel, Info);
}
void ForwardingDiagnosticConsumer::clear() {
DiagnosticConsumer::clear();
Target.clear();
}
bool ForwardingDiagnosticConsumer::IncludeInDiagnosticCounts() const {
return Target.IncludeInDiagnosticCounts();
}
PartialDiagnostic::DiagStorageAllocator::DiagStorageAllocator() {
for (unsigned I = 0; I != NumCached; ++I)
FreeList[I] = Cached + I;
NumFreeListEntries = NumCached;
}
PartialDiagnostic::DiagStorageAllocator::~DiagStorageAllocator() {
assert((NumFreeListEntries == NumCached ||
llvm::CrashRecoveryContext::isRecoveringFromCrash()) &&
"A partial is on the lam");
}
char DiagnosticError::ID;