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