#include "clang/Basic/Diagnostic.h"
#include "clang/Lex/LexDiagnostic.h"
#include "clang/Parse/ParseDiagnostic.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Analysis/AnalysisDiagnostic.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include <vector>
#include <map>
#include <cstring>
using namespace clang;
enum {
CLASS_NOTE = 0x01,
CLASS_WARNING = 0x02,
CLASS_EXTENSION = 0x03,
CLASS_ERROR = 0x04
};
struct StaticDiagInfoRec {
unsigned short DiagID;
unsigned Mapping : 3;
unsigned Class : 3;
const char *Description;
const char *OptionGroup;
bool operator<(const StaticDiagInfoRec &RHS) const {
return DiagID < RHS.DiagID;
}
bool operator>(const StaticDiagInfoRec &RHS) const {
return DiagID > RHS.DiagID;
}
};
static const StaticDiagInfoRec StaticDiagInfo[] = {
#define DIAG(ENUM,CLASS,DEFAULT_MAPPING,DESC,GROUP) \
{ diag::ENUM, DEFAULT_MAPPING, CLASS, DESC, GROUP },
#include "clang/Basic/DiagnosticCommonKinds.inc"
#include "clang/Basic/DiagnosticDriverKinds.inc"
#include "clang/Basic/DiagnosticFrontendKinds.inc"
#include "clang/Basic/DiagnosticLexKinds.inc"
#include "clang/Basic/DiagnosticParseKinds.inc"
#include "clang/Basic/DiagnosticASTKinds.inc"
#include "clang/Basic/DiagnosticSemaKinds.inc"
#include "clang/Basic/DiagnosticAnalysisKinds.inc"
{ 0, 0, 0, 0, 0 }
};
#undef DIAG
static const StaticDiagInfoRec *GetDiagInfo(unsigned DiagID) {
unsigned NumDiagEntries = sizeof(StaticDiagInfo)/sizeof(StaticDiagInfo[0])-1;
#ifndef NDEBUG
static bool IsFirst = true;
if (IsFirst) {
for (unsigned i = 1; i != NumDiagEntries; ++i)
assert(StaticDiagInfo[i-1] < StaticDiagInfo[i] &&
"Improperly sorted diag info");
IsFirst = false;
}
#endif
StaticDiagInfoRec Find = { DiagID, 0, 0, 0, 0 };
const StaticDiagInfoRec *Found =
std::lower_bound(StaticDiagInfo, StaticDiagInfo + NumDiagEntries, Find);
if (Found == StaticDiagInfo + NumDiagEntries ||
Found->DiagID != DiagID)
return 0;
return Found;
}
static unsigned GetDefaultDiagMapping(unsigned DiagID) {
if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID))
return Info->Mapping;
return diag::MAP_FATAL;
}
const char *Diagnostic::getWarningOptionForDiag(unsigned DiagID) {
if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID))
return Info->OptionGroup;
return 0;
}
static unsigned getBuiltinDiagClass(unsigned DiagID) {
if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID))
return Info->Class;
return ~0U;
}
namespace clang {
namespace diag {
class CustomDiagInfo {
typedef std::pair<Diagnostic::Level, std::string> DiagDesc;
std::vector<DiagDesc> DiagInfo;
std::map<DiagDesc, unsigned> DiagIDs;
public:
const char *getDescription(unsigned DiagID) const {
assert(this && DiagID-DIAG_UPPER_LIMIT < DiagInfo.size() &&
"Invalid diagnosic ID");
return DiagInfo[DiagID-DIAG_UPPER_LIMIT].second.c_str();
}
Diagnostic::Level getLevel(unsigned DiagID) const {
assert(this && DiagID-DIAG_UPPER_LIMIT < DiagInfo.size() &&
"Invalid diagnosic ID");
return DiagInfo[DiagID-DIAG_UPPER_LIMIT].first;
}
unsigned getOrCreateDiagID(Diagnostic::Level L, const char *Message,
Diagnostic &Diags) {
DiagDesc D(L, Message);
std::map<DiagDesc, unsigned>::iterator I = DiagIDs.lower_bound(D);
if (I != DiagIDs.end() && I->first == D)
return I->second;
unsigned ID = DiagInfo.size()+DIAG_UPPER_LIMIT;
DiagIDs.insert(std::make_pair(D, ID));
DiagInfo.push_back(D);
return ID;
}
};
} }
static void DummyArgToStringFn(Diagnostic::ArgumentKind AK, intptr_t QT,
const char *Modifier, unsigned ML,
const char *Argument, unsigned ArgLen,
llvm::SmallVectorImpl<char> &Output,
void *Cookie) {
const char *Str = "<can't format argument>";
Output.append(Str, Str+strlen(Str));
}
Diagnostic::Diagnostic(DiagnosticClient *client) : Client(client) {
AllExtensionsSilenced = 0;
IgnoreAllWarnings = false;
WarningsAsErrors = false;
SuppressSystemWarnings = false;
ExtBehavior = Ext_Ignore;
ErrorOccurred = false;
FatalErrorOccurred = false;
NumDiagnostics = 0;
NumErrors = 0;
CustomDiagInfo = 0;
CurDiagID = ~0U;
LastDiagLevel = Ignored;
ArgToStringFn = DummyArgToStringFn;
ArgToStringCookie = 0;
memset(DiagMappings, 0, sizeof(DiagMappings));
}
Diagnostic::~Diagnostic() {
delete CustomDiagInfo;
}
unsigned Diagnostic::getCustomDiagID(Level L, const char *Message) {
if (CustomDiagInfo == 0)
CustomDiagInfo = new diag::CustomDiagInfo();
return CustomDiagInfo->getOrCreateDiagID(L, Message, *this);
}
bool Diagnostic::isBuiltinWarningOrExtension(unsigned DiagID) {
return DiagID < diag::DIAG_UPPER_LIMIT &&
getBuiltinDiagClass(DiagID) != CLASS_ERROR;
}
bool Diagnostic::isBuiltinNote(unsigned DiagID) {
return DiagID < diag::DIAG_UPPER_LIMIT &&
getBuiltinDiagClass(DiagID) == CLASS_NOTE;
}
bool Diagnostic::isBuiltinExtensionDiag(unsigned DiagID) {
return DiagID < diag::DIAG_UPPER_LIMIT &&
getBuiltinDiagClass(DiagID) == CLASS_EXTENSION;
}
const char *Diagnostic::getDescription(unsigned DiagID) const {
if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID))
return Info->Description;
return CustomDiagInfo->getDescription(DiagID);
}
Diagnostic::Level Diagnostic::getDiagnosticLevel(unsigned DiagID) const {
if (DiagID >= diag::DIAG_UPPER_LIMIT)
return CustomDiagInfo->getLevel(DiagID);
unsigned DiagClass = getBuiltinDiagClass(DiagID);
assert(DiagClass != CLASS_NOTE && "Cannot get diagnostic level of a note!");
return getDiagnosticLevel(DiagID, DiagClass);
}
Diagnostic::Level
Diagnostic::getDiagnosticLevel(unsigned DiagID, unsigned DiagClass) const {
Diagnostic::Level Result = Diagnostic::Fatal;
unsigned MappingInfo = getDiagnosticMappingInfo((diag::kind)DiagID);
if (MappingInfo == 0) {
MappingInfo = GetDefaultDiagMapping(DiagID);
setDiagnosticMappingInternal(DiagID, MappingInfo, false);
}
switch (MappingInfo & 7) {
default: assert(0 && "Unknown mapping!");
case diag::MAP_IGNORE:
if (!isBuiltinExtensionDiag(DiagID) || ExtBehavior == Ext_Ignore || (MappingInfo & 8) != 0) return Diagnostic::Ignored;
Result = Diagnostic::Warning;
if (ExtBehavior == Ext_Error) Result = Diagnostic::Error;
break;
case diag::MAP_ERROR:
Result = Diagnostic::Error;
break;
case diag::MAP_FATAL:
Result = Diagnostic::Fatal;
break;
case diag::MAP_WARNING:
if (IgnoreAllWarnings)
return Diagnostic::Ignored;
Result = Diagnostic::Warning;
if (ExtBehavior == Ext_Error &&
(MappingInfo & 8) == 0 &&
isBuiltinExtensionDiag(DiagID))
Result = Diagnostic::Error;
if (WarningsAsErrors)
Result = Diagnostic::Error;
break;
case diag::MAP_WARNING_NO_WERROR:
Result = Diagnostic::Warning;
if (IgnoreAllWarnings)
return Diagnostic::Ignored;
break;
}
if (AllExtensionsSilenced && isBuiltinExtensionDiag(DiagID))
return Diagnostic::Ignored;
return Result;
}
struct WarningOption {
const char *Name;
const short *Members;
const char *SubGroups;
};
#define GET_DIAG_ARRAYS
#include "clang/Basic/DiagnosticGroups.inc"
#undef GET_DIAG_ARRAYS
static const WarningOption OptionTable[] = {
#define GET_DIAG_TABLE
#include "clang/Basic/DiagnosticGroups.inc"
#undef GET_DIAG_TABLE
};
static const size_t OptionTableSize =
sizeof(OptionTable) / sizeof(OptionTable[0]);
static bool WarningOptionCompare(const WarningOption &LHS,
const WarningOption &RHS) {
return strcmp(LHS.Name, RHS.Name) < 0;
}
static void MapGroupMembers(const WarningOption *Group, diag::Mapping Mapping,
Diagnostic &Diags) {
if (const short *Member = Group->Members) {
for (; *Member != -1; ++Member)
Diags.setDiagnosticMapping(*Member, Mapping);
}
if (const char *SubGroups = Group->SubGroups) {
for (; *SubGroups != (char)-1; ++SubGroups)
MapGroupMembers(&OptionTable[(unsigned char)*SubGroups], Mapping, Diags);
}
}
bool Diagnostic::setDiagnosticGroupMapping(const char *Group,
diag::Mapping Map) {
WarningOption Key = { Group, 0, 0 };
const WarningOption *Found =
std::lower_bound(OptionTable, OptionTable + OptionTableSize, Key,
WarningOptionCompare);
if (Found == OptionTable + OptionTableSize ||
strcmp(Found->Name, Group) != 0)
return true;
MapGroupMembers(Found, Map, *this);
return false;
}
void Diagnostic::ProcessDiag() {
DiagnosticInfo Info(this);
Diagnostic::Level DiagLevel;
unsigned DiagID = Info.getID();
bool ShouldEmitInSystemHeader;
if (DiagID >= diag::DIAG_UPPER_LIMIT) {
DiagLevel = CustomDiagInfo->getLevel(DiagID);
ShouldEmitInSystemHeader = true;
} else {
unsigned DiagClass = getBuiltinDiagClass(DiagID);
if (DiagClass == CLASS_NOTE) {
DiagLevel = Diagnostic::Note;
ShouldEmitInSystemHeader = false; } else {
ShouldEmitInSystemHeader = DiagClass == CLASS_ERROR;
DiagLevel = getDiagnosticLevel(DiagID, DiagClass);
}
}
if (DiagLevel != Diagnostic::Note) {
if (LastDiagLevel == Diagnostic::Fatal)
FatalErrorOccurred = true;
LastDiagLevel = DiagLevel;
}
if (FatalErrorOccurred)
return;
if (DiagLevel == Diagnostic::Ignored ||
(DiagLevel == Diagnostic::Note && LastDiagLevel == Diagnostic::Ignored))
return;
if (SuppressSystemWarnings && !ShouldEmitInSystemHeader &&
Info.getLocation().isValid() &&
Info.getLocation().getSpellingLoc().isInSystemHeader() &&
(DiagLevel != Diagnostic::Note || LastDiagLevel == Diagnostic::Ignored)) {
LastDiagLevel = Diagnostic::Ignored;
return;
}
if (DiagLevel >= Diagnostic::Error) {
ErrorOccurred = true;
++NumErrors;
}
Client->HandleDiagnostic(DiagLevel, Info);
if (Client->IncludeInDiagnosticCounts()) ++NumDiagnostics;
CurDiagID = ~0U;
}
DiagnosticClient::~DiagnosticClient() {}
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);
}
static void HandleSelectModifier(unsigned ValNo,
const char *Argument, unsigned ArgumentLen,
llvm::SmallVectorImpl<char> &OutStr) {
const char *ArgumentEnd = Argument+ArgumentLen;
while (ValNo) {
const char *NextVal = std::find(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 = std::find(Argument, ArgumentEnd, '|');
OutStr.append(Argument, EndPtr);
}
static void HandleIntegerSModifier(unsigned ValNo,
llvm::SmallVectorImpl<char> &OutStr) {
if (ValNo != 1)
OutStr.push_back('s');
}
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 (1) {
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(unsigned ValNo,
const char *Argument, unsigned ArgumentLen,
llvm::SmallVectorImpl<char> &OutStr) {
const char *ArgumentEnd = Argument + ArgumentLen;
while (1) {
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 = std::find(Argument, ArgumentEnd, '|');
OutStr.append(Argument, ExprEnd);
return;
}
Argument = std::find(Argument, ArgumentEnd - 1, '|') + 1;
}
}
void DiagnosticInfo::
FormatDiagnostic(llvm::SmallVectorImpl<char> &OutStr) const {
const char *DiagStr = getDiags()->getDescription(getID());
const char *DiagEnd = DiagStr+strlen(DiagStr);
while (DiagStr != DiagEnd) {
if (DiagStr[0] != '%') {
const char *StrEnd = std::find(DiagStr, DiagEnd, '%');
OutStr.append(DiagStr, StrEnd);
DiagStr = StrEnd;
continue;
} else if (DiagStr[1] == '%') {
OutStr.push_back('%'); DiagStr += 2;
continue;
}
++DiagStr;
const char *Modifier = 0, *Argument = 0;
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;
for (; DiagStr[0] != '}'; ++DiagStr)
assert(DiagStr[0] && "Mismatched {}'s in diagnostic string!");
ArgumentLen = DiagStr-Argument;
++DiagStr; }
}
assert(isdigit(*DiagStr) && "Invalid format for argument in diagnostic");
unsigned ArgNo = *DiagStr++ - '0';
switch (getArgKind(ArgNo)) {
case Diagnostic::ak_std_string: {
const std::string &S = getArgStdStr(ArgNo);
assert(ModifierLen == 0 && "No modifiers for strings yet");
OutStr.append(S.begin(), S.end());
break;
}
case Diagnostic::ak_c_string: {
const char *S = getArgCStr(ArgNo);
assert(ModifierLen == 0 && "No modifiers for strings yet");
if (!S)
S = "(null)";
OutStr.append(S, S + strlen(S));
break;
}
case Diagnostic::ak_sint: {
int Val = getArgSInt(ArgNo);
if (ModifierIs(Modifier, ModifierLen, "select")) {
HandleSelectModifier((unsigned)Val, Argument, ArgumentLen, OutStr);
} else if (ModifierIs(Modifier, ModifierLen, "s")) {
HandleIntegerSModifier(Val, OutStr);
} else if (ModifierIs(Modifier, ModifierLen, "plural")) {
HandlePluralModifier((unsigned)Val, Argument, ArgumentLen, OutStr);
} else {
assert(ModifierLen == 0 && "Unknown integer modifier");
std::string S = llvm::itostr(Val);
OutStr.append(S.begin(), S.end());
}
break;
}
case Diagnostic::ak_uint: {
unsigned Val = getArgUInt(ArgNo);
if (ModifierIs(Modifier, ModifierLen, "select")) {
HandleSelectModifier(Val, Argument, ArgumentLen, OutStr);
} else if (ModifierIs(Modifier, ModifierLen, "s")) {
HandleIntegerSModifier(Val, OutStr);
} else if (ModifierIs(Modifier, ModifierLen, "plural")) {
HandlePluralModifier((unsigned)Val, Argument, ArgumentLen, OutStr);
} else {
assert(ModifierLen == 0 && "Unknown integer modifier");
std::string S = llvm::utostr_32(Val);
OutStr.append(S.begin(), S.end());
}
break;
}
case Diagnostic::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;
}
OutStr.push_back('\'');
OutStr.append(II->getName(), II->getName() + II->getLength());
OutStr.push_back('\'');
break;
}
case Diagnostic::ak_qualtype:
case Diagnostic::ak_declarationname:
case Diagnostic::ak_nameddecl:
getDiags()->ConvertArgToString(getArgKind(ArgNo), getRawArg(ArgNo),
Modifier, ModifierLen,
Argument, ArgumentLen, OutStr);
break;
}
}
}
bool DiagnosticClient::IncludeInDiagnosticCounts() const { return true; }