Home | History | Annotate | Download | only in TableGen
      1 //=- ClangDiagnosticsEmitter.cpp - Generate Clang diagnostics tables -*- C++ -*-
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // These tablegen backends emit Clang diagnostics tables.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "ClangDiagnosticsEmitter.h"
     15 #include "llvm/TableGen/Record.h"
     16 #include "llvm/Support/Debug.h"
     17 #include "llvm/Support/Compiler.h"
     18 #include "llvm/ADT/DenseSet.h"
     19 #include "llvm/ADT/StringMap.h"
     20 #include "llvm/ADT/SmallString.h"
     21 #include <map>
     22 #include <algorithm>
     23 #include <functional>
     24 #include <set>
     25 using namespace llvm;
     26 
     27 //===----------------------------------------------------------------------===//
     28 // Diagnostic category computation code.
     29 //===----------------------------------------------------------------------===//
     30 
     31 namespace {
     32 class DiagGroupParentMap {
     33   RecordKeeper &Records;
     34   std::map<const Record*, std::vector<Record*> > Mapping;
     35 public:
     36   DiagGroupParentMap(RecordKeeper &records) : Records(records) {
     37     std::vector<Record*> DiagGroups
     38       = Records.getAllDerivedDefinitions("DiagGroup");
     39     for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) {
     40       std::vector<Record*> SubGroups =
     41         DiagGroups[i]->getValueAsListOfDefs("SubGroups");
     42       for (unsigned j = 0, e = SubGroups.size(); j != e; ++j)
     43         Mapping[SubGroups[j]].push_back(DiagGroups[i]);
     44     }
     45   }
     46 
     47   const std::vector<Record*> &getParents(const Record *Group) {
     48     return Mapping[Group];
     49   }
     50 };
     51 } // end anonymous namespace.
     52 
     53 static std::string
     54 getCategoryFromDiagGroup(const Record *Group,
     55                          DiagGroupParentMap &DiagGroupParents) {
     56   // If the DiagGroup has a category, return it.
     57   std::string CatName = Group->getValueAsString("CategoryName");
     58   if (!CatName.empty()) return CatName;
     59 
     60   // The diag group may the subgroup of one or more other diagnostic groups,
     61   // check these for a category as well.
     62   const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
     63   for (unsigned i = 0, e = Parents.size(); i != e; ++i) {
     64     CatName = getCategoryFromDiagGroup(Parents[i], DiagGroupParents);
     65     if (!CatName.empty()) return CatName;
     66   }
     67   return "";
     68 }
     69 
     70 /// getDiagnosticCategory - Return the category that the specified diagnostic
     71 /// lives in.
     72 static std::string getDiagnosticCategory(const Record *R,
     73                                          DiagGroupParentMap &DiagGroupParents) {
     74   // If the diagnostic is in a group, and that group has a category, use it.
     75   if (DefInit *Group = dynamic_cast<DefInit*>(R->getValueInit("Group"))) {
     76     // Check the diagnostic's diag group for a category.
     77     std::string CatName = getCategoryFromDiagGroup(Group->getDef(),
     78                                                    DiagGroupParents);
     79     if (!CatName.empty()) return CatName;
     80   }
     81 
     82   // If the diagnostic itself has a category, get it.
     83   return R->getValueAsString("CategoryName");
     84 }
     85 
     86 namespace {
     87   class DiagCategoryIDMap {
     88     RecordKeeper &Records;
     89     StringMap<unsigned> CategoryIDs;
     90     std::vector<std::string> CategoryStrings;
     91   public:
     92     DiagCategoryIDMap(RecordKeeper &records) : Records(records) {
     93       DiagGroupParentMap ParentInfo(Records);
     94 
     95       // The zero'th category is "".
     96       CategoryStrings.push_back("");
     97       CategoryIDs[""] = 0;
     98 
     99       std::vector<Record*> Diags =
    100       Records.getAllDerivedDefinitions("Diagnostic");
    101       for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
    102         std::string Category = getDiagnosticCategory(Diags[i], ParentInfo);
    103         if (Category.empty()) continue;  // Skip diags with no category.
    104 
    105         unsigned &ID = CategoryIDs[Category];
    106         if (ID != 0) continue;  // Already seen.
    107 
    108         ID = CategoryStrings.size();
    109         CategoryStrings.push_back(Category);
    110       }
    111     }
    112 
    113     unsigned getID(StringRef CategoryString) {
    114       return CategoryIDs[CategoryString];
    115     }
    116 
    117     typedef std::vector<std::string>::iterator iterator;
    118     iterator begin() { return CategoryStrings.begin(); }
    119     iterator end() { return CategoryStrings.end(); }
    120   };
    121 
    122   struct GroupInfo {
    123     std::vector<const Record*> DiagsInGroup;
    124     std::vector<std::string> SubGroups;
    125     unsigned IDNo;
    126   };
    127 } // end anonymous namespace.
    128 
    129 /// \brief Invert the 1-[0/1] mapping of diags to group into a one to many
    130 /// mapping of groups to diags in the group.
    131 static void groupDiagnostics(const std::vector<Record*> &Diags,
    132                              const std::vector<Record*> &DiagGroups,
    133                              std::map<std::string, GroupInfo> &DiagsInGroup) {
    134   for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
    135     const Record *R = Diags[i];
    136     DefInit *DI = dynamic_cast<DefInit*>(R->getValueInit("Group"));
    137     if (DI == 0) continue;
    138     std::string GroupName = DI->getDef()->getValueAsString("GroupName");
    139     DiagsInGroup[GroupName].DiagsInGroup.push_back(R);
    140   }
    141 
    142   // Add all DiagGroup's to the DiagsInGroup list to make sure we pick up empty
    143   // groups (these are warnings that GCC supports that clang never produces).
    144   for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) {
    145     Record *Group = DiagGroups[i];
    146     GroupInfo &GI = DiagsInGroup[Group->getValueAsString("GroupName")];
    147 
    148     std::vector<Record*> SubGroups = Group->getValueAsListOfDefs("SubGroups");
    149     for (unsigned j = 0, e = SubGroups.size(); j != e; ++j)
    150       GI.SubGroups.push_back(SubGroups[j]->getValueAsString("GroupName"));
    151   }
    152 
    153   // Assign unique ID numbers to the groups.
    154   unsigned IDNo = 0;
    155   for (std::map<std::string, GroupInfo>::iterator
    156        I = DiagsInGroup.begin(), E = DiagsInGroup.end(); I != E; ++I, ++IDNo)
    157     I->second.IDNo = IDNo;
    158 }
    159 
    160 //===----------------------------------------------------------------------===//
    161 // Warning Tables (.inc file) generation.
    162 //===----------------------------------------------------------------------===//
    163 
    164 void ClangDiagsDefsEmitter::run(raw_ostream &OS) {
    165   // Write the #if guard
    166   if (!Component.empty()) {
    167     std::string ComponentName = StringRef(Component).upper();
    168     OS << "#ifdef " << ComponentName << "START\n";
    169     OS << "__" << ComponentName << "START = DIAG_START_" << ComponentName
    170        << ",\n";
    171     OS << "#undef " << ComponentName << "START\n";
    172     OS << "#endif\n\n";
    173   }
    174 
    175   const std::vector<Record*> &Diags =
    176     Records.getAllDerivedDefinitions("Diagnostic");
    177 
    178   std::vector<Record*> DiagGroups
    179     = Records.getAllDerivedDefinitions("DiagGroup");
    180 
    181   std::map<std::string, GroupInfo> DiagsInGroup;
    182   groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
    183 
    184   DiagCategoryIDMap CategoryIDs(Records);
    185   DiagGroupParentMap DGParentMap(Records);
    186 
    187   for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
    188     const Record &R = *Diags[i];
    189     // Filter by component.
    190     if (!Component.empty() && Component != R.getValueAsString("Component"))
    191       continue;
    192 
    193     OS << "DIAG(" << R.getName() << ", ";
    194     OS << R.getValueAsDef("Class")->getName();
    195     OS << ", diag::" << R.getValueAsDef("DefaultMapping")->getName();
    196 
    197     // Description string.
    198     OS << ", \"";
    199     OS.write_escaped(R.getValueAsString("Text")) << '"';
    200 
    201     // Warning associated with the diagnostic. This is stored as an index into
    202     // the alphabetically sorted warning table.
    203     if (DefInit *DI = dynamic_cast<DefInit*>(R.getValueInit("Group"))) {
    204       std::map<std::string, GroupInfo>::iterator I =
    205           DiagsInGroup.find(DI->getDef()->getValueAsString("GroupName"));
    206       assert(I != DiagsInGroup.end());
    207       OS << ", " << I->second.IDNo;
    208     } else {
    209       OS << ", 0";
    210     }
    211 
    212     // SFINAE bit
    213     if (R.getValueAsBit("SFINAE"))
    214       OS << ", true";
    215     else
    216       OS << ", false";
    217 
    218     // Access control bit
    219     if (R.getValueAsBit("AccessControl"))
    220       OS << ", true";
    221     else
    222       OS << ", false";
    223 
    224     // FIXME: This condition is just to avoid temporary revlock, it can be
    225     // removed.
    226     if (R.getValue("WarningNoWerror")) {
    227       // Default warning has no Werror bit.
    228       if (R.getValueAsBit("WarningNoWerror"))
    229         OS << ", true";
    230       else
    231         OS << ", false";
    232 
    233       // Default warning show in system header bit.
    234       if (R.getValueAsBit("WarningShowInSystemHeader"))
    235         OS << ", true";
    236       else
    237         OS << ", false";
    238     }
    239 
    240     // Category number.
    241     OS << ", " << CategoryIDs.getID(getDiagnosticCategory(&R, DGParentMap));
    242     OS << ")\n";
    243   }
    244 }
    245 
    246 //===----------------------------------------------------------------------===//
    247 // Warning Group Tables generation
    248 //===----------------------------------------------------------------------===//
    249 
    250 static std::string getDiagCategoryEnum(llvm::StringRef name) {
    251   if (name.empty())
    252     return "DiagCat_None";
    253   SmallString<256> enumName = llvm::StringRef("DiagCat_");
    254   for (llvm::StringRef::iterator I = name.begin(), E = name.end(); I != E; ++I)
    255     enumName += isalnum(*I) ? *I : '_';
    256   return enumName.str();
    257 }
    258 
    259 void ClangDiagGroupsEmitter::run(raw_ostream &OS) {
    260   // Compute a mapping from a DiagGroup to all of its parents.
    261   DiagGroupParentMap DGParentMap(Records);
    262 
    263   std::vector<Record*> Diags =
    264     Records.getAllDerivedDefinitions("Diagnostic");
    265 
    266   std::vector<Record*> DiagGroups
    267     = Records.getAllDerivedDefinitions("DiagGroup");
    268 
    269   std::map<std::string, GroupInfo> DiagsInGroup;
    270   groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
    271 
    272   // Walk through the groups emitting an array for each diagnostic of the diags
    273   // that are mapped to.
    274   OS << "\n#ifdef GET_DIAG_ARRAYS\n";
    275   unsigned MaxLen = 0;
    276   for (std::map<std::string, GroupInfo>::iterator
    277        I = DiagsInGroup.begin(), E = DiagsInGroup.end(); I != E; ++I) {
    278     MaxLen = std::max(MaxLen, (unsigned)I->first.size());
    279 
    280     std::vector<const Record*> &V = I->second.DiagsInGroup;
    281     if (!V.empty()) {
    282       OS << "static const short DiagArray" << I->second.IDNo << "[] = { ";
    283       for (unsigned i = 0, e = V.size(); i != e; ++i)
    284         OS << "diag::" << V[i]->getName() << ", ";
    285       OS << "-1 };\n";
    286     }
    287 
    288     const std::vector<std::string> &SubGroups = I->second.SubGroups;
    289     if (!SubGroups.empty()) {
    290       OS << "static const short DiagSubGroup" << I->second.IDNo << "[] = { ";
    291       for (unsigned i = 0, e = SubGroups.size(); i != e; ++i) {
    292         std::map<std::string, GroupInfo>::iterator RI =
    293           DiagsInGroup.find(SubGroups[i]);
    294         assert(RI != DiagsInGroup.end() && "Referenced without existing?");
    295         OS << RI->second.IDNo << ", ";
    296       }
    297       OS << "-1 };\n";
    298     }
    299   }
    300   OS << "#endif // GET_DIAG_ARRAYS\n\n";
    301 
    302   // Emit the table now.
    303   OS << "\n#ifdef GET_DIAG_TABLE\n";
    304   for (std::map<std::string, GroupInfo>::iterator
    305        I = DiagsInGroup.begin(), E = DiagsInGroup.end(); I != E; ++I) {
    306     // Group option string.
    307     OS << "  { ";
    308     OS << I->first.size() << ", ";
    309     OS << "\"";
    310     if (I->first.find_first_not_of("abcdefghijklmnopqrstuvwxyz"
    311                                    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    312                                    "0123456789!@#$%^*-+=:?")!=std::string::npos)
    313       throw "Invalid character in diagnostic group '" + I->first + "'";
    314     OS.write_escaped(I->first) << "\","
    315                                << std::string(MaxLen-I->first.size()+1, ' ');
    316 
    317     // Diagnostics in the group.
    318     if (I->second.DiagsInGroup.empty())
    319       OS << "0, ";
    320     else
    321       OS << "DiagArray" << I->second.IDNo << ", ";
    322 
    323     // Subgroups.
    324     if (I->second.SubGroups.empty())
    325       OS << 0;
    326     else
    327       OS << "DiagSubGroup" << I->second.IDNo;
    328     OS << " },\n";
    329   }
    330   OS << "#endif // GET_DIAG_TABLE\n\n";
    331 
    332   // Emit the category table next.
    333   DiagCategoryIDMap CategoriesByID(Records);
    334   OS << "\n#ifdef GET_CATEGORY_TABLE\n";
    335   for (DiagCategoryIDMap::iterator I = CategoriesByID.begin(),
    336        E = CategoriesByID.end(); I != E; ++I)
    337     OS << "CATEGORY(\"" << *I << "\", " << getDiagCategoryEnum(*I) << ")\n";
    338   OS << "#endif // GET_CATEGORY_TABLE\n\n";
    339 }
    340 
    341 //===----------------------------------------------------------------------===//
    342 // Diagnostic name index generation
    343 //===----------------------------------------------------------------------===//
    344 
    345 namespace {
    346 struct RecordIndexElement
    347 {
    348   RecordIndexElement() {}
    349   explicit RecordIndexElement(Record const &R):
    350     Name(R.getName()) {}
    351 
    352   std::string Name;
    353 };
    354 
    355 struct RecordIndexElementSorter :
    356   public std::binary_function<RecordIndexElement, RecordIndexElement, bool> {
    357 
    358   bool operator()(RecordIndexElement const &Lhs,
    359                   RecordIndexElement const &Rhs) const {
    360     return Lhs.Name < Rhs.Name;
    361   }
    362 
    363 };
    364 
    365 } // end anonymous namespace.
    366 
    367 void ClangDiagsIndexNameEmitter::run(raw_ostream &OS) {
    368   const std::vector<Record*> &Diags =
    369     Records.getAllDerivedDefinitions("Diagnostic");
    370 
    371   std::vector<RecordIndexElement> Index;
    372   Index.reserve(Diags.size());
    373   for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
    374     const Record &R = *(Diags[i]);
    375     Index.push_back(RecordIndexElement(R));
    376   }
    377 
    378   std::sort(Index.begin(), Index.end(), RecordIndexElementSorter());
    379 
    380   for (unsigned i = 0, e = Index.size(); i != e; ++i) {
    381     const RecordIndexElement &R = Index[i];
    382 
    383     OS << "DIAG_NAME_INDEX(" << R.Name << ")\n";
    384   }
    385 }
    386