//=- ClangDiagnosticsEmitter.cpp - Generate Clang diagnostics tables -*- C++ -*- // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // These tablegen backends emit Clang diagnostics tables. // //===----------------------------------------------------------------------===// #include "ClangDiagnosticsEmitter.h" #include "Record.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Streams.h" #include "llvm/ADT/VectorExtras.h" #include "llvm/ADT/DenseSet.h" #include #include using namespace llvm; //===----------------------------------------------------------------------===// // Generic routines for all Clang TableGen backends. //===----------------------------------------------------------------------===// typedef std::vector RecordVector; typedef std::vector SuperClassVector; typedef std::vector RecordValVector; static const RecordVal* findRecordVal(const Record& R, const std::string &key) { const RecordValVector &Vals = R.getValues(); for (RecordValVector::const_iterator I=Vals.begin(), E=Vals.end(); I!=E; ++I) if ((*I).getName() == key) return &*I; return 0; } static const Record* getDiagKind(const Record* DiagClass, const Record &R) { const SuperClassVector &SC = R.getSuperClasses(); for (SuperClassVector::const_iterator I=SC.begin(), E=SC.end(); I!=E; ++I) if ((*I)->isSubClassOf(DiagClass) && (*I)->getName() != "DiagnosticControlled") return *I; return 0; } static void EmitEscaped(std::ostream& OS, const std::string &s) { for (std::string::const_iterator I=s.begin(), E=s.end(); I!=E; ++I) switch (*I) { default: OS << *I; break; case '\"': OS << "\\" << *I; break; case '\\': OS << "\\\\"; break; } } static void EmitAllCaps(std::ostream& OS, const std::string &s) { for (std::string::const_iterator I=s.begin(), E=s.end(); I!=E; ++I) OS << char(toupper(*I)); } //===----------------------------------------------------------------------===// // Warning Tables (.inc file) generation. //===----------------------------------------------------------------------===// static void ProcessDiag(std::ostream& OS, const Record* DiagClass, const Record& R) { const Record* DiagKind = getDiagKind(DiagClass, R); if (!DiagKind) return; OS << "DIAG(" << R.getName() << ", "; EmitAllCaps(OS, DiagKind->getName()); const RecordVal* Text = findRecordVal(R, "Text"); assert(Text && "No 'Text' entry in Diagnostic."); const StringInit* TextVal = dynamic_cast(Text->getValue()); assert(TextVal && "Value 'Text' must be a string."); OS << ", \""; EmitEscaped(OS, TextVal->getValue()); OS << "\")\n"; } void ClangDiagsDefsEmitter::run(std::ostream &OS) { const RecordVector &Diags = Records.getAllDerivedDefinitions("Diagnostic"); const Record* DiagClass = Records.getClass("Diagnostic"); assert(DiagClass && "No Diagnostic class defined."); // Write the #if guard if (!Component.empty()) { OS << "#ifdef "; EmitAllCaps(OS, Component); OS << "START\n__"; EmitAllCaps(OS, Component); OS << "START = DIAG_START_"; EmitAllCaps(OS, Component); OS << ",\n#undef "; EmitAllCaps(OS, Component); OS << "START\n#endif\n"; } for (RecordVector::const_iterator I=Diags.begin(), E=Diags.end(); I!=E; ++I) { if (!Component.empty()) { const RecordVal* V = findRecordVal(**I, "Component"); if (!V) continue; const StringInit* SV = dynamic_cast(V->getValue()); if (!SV || SV->getValue() != Component) continue; } ProcessDiag(OS, DiagClass, **I); } } //===----------------------------------------------------------------------===// // Warning Group Tables generation. //===----------------------------------------------------------------------===// static const std::string &getOptName(const Record *R) { const RecordVal *V = findRecordVal(*R, "Name"); assert(V && "Options must have a 'Name' value."); const StringInit* SV = dynamic_cast(V->getValue()); assert(SV && "'Name' entry must be a string."); return SV->getValue(); } namespace { struct VISIBILITY_HIDDEN CompareOptName { bool operator()(const Record* A, const Record* B) const { return getOptName(A) < getOptName(B); } }; } typedef std::set DiagnosticSet; typedef std::map OptionMap; typedef llvm::DenseSet VisitedLists; static void BuildGroup(DiagnosticSet& DS, VisitedLists &Visited, const Init* X); static void BuildGroup(DiagnosticSet &DS, VisitedLists &Visited, const ListInit* LV) { // Simple hack to prevent including a list multiple times. This may be useful // if one declares an Option by including a bunch of other Options that // include other Options, etc. if (Visited.count(LV)) return; Visited.insert(LV); // Iterate through the list and grab all DiagnosticControlled. for (ListInit::const_iterator I = LV->begin(), E = LV->end(); I!=E; ++I) BuildGroup(DS, Visited, *I); } static void BuildGroup(DiagnosticSet& DS, VisitedLists &Visited, const Record *Def) { // If an Option includes another Option, inline the Diagnostics of the // included Option. if (Def->isSubClassOf("Option")) { if (const RecordVal* V = findRecordVal(*Def, "Members")) if (const ListInit* LV = dynamic_cast(V->getValue())) BuildGroup(DS, Visited, LV); return; } if (Def->isSubClassOf("DiagnosticControlled")) DS.insert(Def); } static void BuildGroup(DiagnosticSet& DS, VisitedLists &Visited, const Init* X) { if (const DefInit *D = dynamic_cast(X)) BuildGroup(DS, Visited, D->getDef()); // We may have some other cases here in the future. } void ClangOptionsEmitter::run(std::ostream &OS) { // Build up a map from options to controlled diagnostics. OptionMap OM; const RecordVector &Opts = Records.getAllDerivedDefinitions("Option"); for (RecordVector::const_iterator I=Opts.begin(), E=Opts.end(); I!=E; ++I) if (const RecordVal* V = findRecordVal(**I, "Members")) if (const ListInit* LV = dynamic_cast(V->getValue())) { VisitedLists Visited; BuildGroup(OM[*I], Visited, LV); } // Iterate through the OptionMap and emit the declarations. for (OptionMap::iterator I = OM.begin(), E = OM.end(); I!=E; ++I) { // Output the option. OS << "static const diag::kind " << I->first->getName() << "[] = { "; DiagnosticSet &DS = I->second; bool first = true; for (DiagnosticSet::iterator I2 = DS.begin(), E2 = DS.end(); I2!=E2; ++I2) { if (first) first = false; else OS << ", "; OS << "diag::" << (*I2)->getName(); } OS << " };\n"; } // Now emit the OptionTable table. OS << "\nstatic const WarningOption OptionTable[] = {"; bool first = true; for (OptionMap::iterator I = OM.begin(), E = OM.end(); I!=E; ++I) { if (first) first = false; else OS << ','; OS << "\n {\"" << getOptName(I->first) << "\", DIAGS(" << I->first->getName() << ")}"; } OS << "\n};\n"; }