ClangDiagnosticsEmitter.cpp revision 36d592718ff342f762e32cbde73d1113f88cb275
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> 24using namespace llvm; 25 26//===----------------------------------------------------------------------===// 27// Diagnostic category computation code. 28//===----------------------------------------------------------------------===// 29 30namespace { 31class DiagGroupParentMap { 32 RecordKeeper &Records; 33 std::map<const Record*, std::vector<Record*> > Mapping; 34public: 35 DiagGroupParentMap(RecordKeeper &records) : Records(records) { 36 std::vector<Record*> DiagGroups 37 = Records.getAllDerivedDefinitions("DiagGroup"); 38 for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) { 39 std::vector<Record*> SubGroups = 40 DiagGroups[i]->getValueAsListOfDefs("SubGroups"); 41 for (unsigned j = 0, e = SubGroups.size(); j != e; ++j) 42 Mapping[SubGroups[j]].push_back(DiagGroups[i]); 43 } 44 } 45 46 const std::vector<Record*> &getParents(const Record *Group) { 47 return Mapping[Group]; 48 } 49}; 50} // end anonymous namespace. 51 52 53static std::string 54getCategoryFromDiagGroup(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. 72static 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 86namespace { 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} // end anonymous namespace. 122 123 124//===----------------------------------------------------------------------===// 125// Warning Tables (.inc file) generation. 126//===----------------------------------------------------------------------===// 127 128void ClangDiagsDefsEmitter::run(raw_ostream &OS) { 129 // Write the #if guard 130 if (!Component.empty()) { 131 std::string ComponentName = StringRef(Component).upper(); 132 OS << "#ifdef " << ComponentName << "START\n"; 133 OS << "__" << ComponentName << "START = DIAG_START_" << ComponentName 134 << ",\n"; 135 OS << "#undef " << ComponentName << "START\n"; 136 OS << "#endif\n\n"; 137 } 138 139 const std::vector<Record*> &Diags = 140 Records.getAllDerivedDefinitions("Diagnostic"); 141 142 DiagCategoryIDMap CategoryIDs(Records); 143 DiagGroupParentMap DGParentMap(Records); 144 145 for (unsigned i = 0, e = Diags.size(); i != e; ++i) { 146 const Record &R = *Diags[i]; 147 // Filter by component. 148 if (!Component.empty() && Component != R.getValueAsString("Component")) 149 continue; 150 151 OS << "DIAG(" << R.getName() << ", "; 152 OS << R.getValueAsDef("Class")->getName(); 153 OS << ", diag::" << R.getValueAsDef("DefaultMapping")->getName(); 154 155 // Description string. 156 OS << ", \""; 157 OS.write_escaped(R.getValueAsString("Text")) << '"'; 158 159 // Warning associated with the diagnostic. 160 if (DefInit *DI = dynamic_cast<DefInit*>(R.getValueInit("Group"))) { 161 OS << ", \""; 162 OS.write_escaped(DI->getDef()->getValueAsString("GroupName")) << '"'; 163 } else { 164 OS << ", \"\""; 165 } 166 167 // SFINAE bit 168 if (R.getValueAsBit("SFINAE")) 169 OS << ", true"; 170 else 171 OS << ", false"; 172 173 // Access control bit 174 if (R.getValueAsBit("AccessControl")) 175 OS << ", true"; 176 else 177 OS << ", false"; 178 179 // FIXME: This condition is just to avoid temporary revlock, it can be 180 // removed. 181 if (R.getValue("WarningNoWerror")) { 182 // Default warning has no Werror bit. 183 if (R.getValueAsBit("WarningNoWerror")) 184 OS << ", true"; 185 else 186 OS << ", false"; 187 188 // Default warning show in system header bit. 189 if (R.getValueAsBit("WarningShowInSystemHeader")) 190 OS << ", true"; 191 else 192 OS << ", false"; 193 } 194 195 // Category number. 196 OS << ", " << CategoryIDs.getID(getDiagnosticCategory(&R, DGParentMap)); 197 OS << ")\n"; 198 } 199} 200 201//===----------------------------------------------------------------------===// 202// Warning Group Tables generation 203//===----------------------------------------------------------------------===// 204 205static std::string getDiagCategoryEnum(llvm::StringRef name) { 206 if (name.empty()) 207 return "DiagCat_None"; 208 SmallString<256> enumName = llvm::StringRef("DiagCat_"); 209 for (llvm::StringRef::iterator I = name.begin(), E = name.end(); I != E; ++I) 210 enumName += isalnum(*I) ? *I : '_'; 211 return enumName.str(); 212} 213 214namespace { 215struct GroupInfo { 216 std::vector<const Record*> DiagsInGroup; 217 std::vector<std::string> SubGroups; 218 unsigned IDNo; 219}; 220} // end anonymous namespace. 221 222void ClangDiagGroupsEmitter::run(raw_ostream &OS) { 223 // Compute a mapping from a DiagGroup to all of its parents. 224 DiagGroupParentMap DGParentMap(Records); 225 226 // Invert the 1-[0/1] mapping of diags to group into a one to many mapping of 227 // groups to diags in the group. 228 std::map<std::string, GroupInfo> DiagsInGroup; 229 230 std::vector<Record*> Diags = 231 Records.getAllDerivedDefinitions("Diagnostic"); 232 for (unsigned i = 0, e = Diags.size(); i != e; ++i) { 233 const Record *R = Diags[i]; 234 DefInit *DI = dynamic_cast<DefInit*>(R->getValueInit("Group")); 235 if (DI == 0) continue; 236 std::string GroupName = DI->getDef()->getValueAsString("GroupName"); 237 DiagsInGroup[GroupName].DiagsInGroup.push_back(R); 238 } 239 240 // Add all DiagGroup's to the DiagsInGroup list to make sure we pick up empty 241 // groups (these are warnings that GCC supports that clang never produces). 242 std::vector<Record*> DiagGroups 243 = Records.getAllDerivedDefinitions("DiagGroup"); 244 for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) { 245 Record *Group = DiagGroups[i]; 246 GroupInfo &GI = DiagsInGroup[Group->getValueAsString("GroupName")]; 247 248 std::vector<Record*> SubGroups = Group->getValueAsListOfDefs("SubGroups"); 249 for (unsigned j = 0, e = SubGroups.size(); j != e; ++j) 250 GI.SubGroups.push_back(SubGroups[j]->getValueAsString("GroupName")); 251 } 252 253 // Assign unique ID numbers to the groups. 254 unsigned IDNo = 0; 255 for (std::map<std::string, GroupInfo>::iterator 256 I = DiagsInGroup.begin(), E = DiagsInGroup.end(); I != E; ++I, ++IDNo) 257 I->second.IDNo = IDNo; 258 259 // Walk through the groups emitting an array for each diagnostic of the diags 260 // that are mapped to. 261 OS << "\n#ifdef GET_DIAG_ARRAYS\n"; 262 unsigned MaxLen = 0; 263 for (std::map<std::string, GroupInfo>::iterator 264 I = DiagsInGroup.begin(), E = DiagsInGroup.end(); I != E; ++I) { 265 MaxLen = std::max(MaxLen, (unsigned)I->first.size()); 266 267 std::vector<const Record*> &V = I->second.DiagsInGroup; 268 if (!V.empty()) { 269 OS << "static const short DiagArray" << I->second.IDNo << "[] = { "; 270 for (unsigned i = 0, e = V.size(); i != e; ++i) 271 OS << "diag::" << V[i]->getName() << ", "; 272 OS << "-1 };\n"; 273 } 274 275 const std::vector<std::string> &SubGroups = I->second.SubGroups; 276 if (!SubGroups.empty()) { 277 OS << "static const short DiagSubGroup" << I->second.IDNo << "[] = { "; 278 for (unsigned i = 0, e = SubGroups.size(); i != e; ++i) { 279 std::map<std::string, GroupInfo>::iterator RI = 280 DiagsInGroup.find(SubGroups[i]); 281 assert(RI != DiagsInGroup.end() && "Referenced without existing?"); 282 OS << RI->second.IDNo << ", "; 283 } 284 OS << "-1 };\n"; 285 } 286 } 287 OS << "#endif // GET_DIAG_ARRAYS\n\n"; 288 289 // Emit the table now. 290 OS << "\n#ifdef GET_DIAG_TABLE\n"; 291 for (std::map<std::string, GroupInfo>::iterator 292 I = DiagsInGroup.begin(), E = DiagsInGroup.end(); I != E; ++I) { 293 // Group option string. 294 OS << " { "; 295 OS << I->first.size() << ", "; 296 OS << "\""; 297 if (I->first.find_first_not_of("abcdefghijklmnopqrstuvwxyz" 298 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 299 "0123456789!@#$%^*-+=:?")!=std::string::npos) 300 throw "Invalid character in diagnostic group '" + I->first + "'"; 301 OS.write_escaped(I->first) << "\"," 302 << std::string(MaxLen-I->first.size()+1, ' '); 303 304 // Diagnostics in the group. 305 if (I->second.DiagsInGroup.empty()) 306 OS << "0, "; 307 else 308 OS << "DiagArray" << I->second.IDNo << ", "; 309 310 // Subgroups. 311 if (I->second.SubGroups.empty()) 312 OS << 0; 313 else 314 OS << "DiagSubGroup" << I->second.IDNo; 315 OS << " },\n"; 316 } 317 OS << "#endif // GET_DIAG_TABLE\n\n"; 318 319 // Emit the category table next. 320 DiagCategoryIDMap CategoriesByID(Records); 321 OS << "\n#ifdef GET_CATEGORY_TABLE\n"; 322 for (DiagCategoryIDMap::iterator I = CategoriesByID.begin(), 323 E = CategoriesByID.end(); I != E; ++I) 324 OS << "CATEGORY(\"" << *I << "\", " << getDiagCategoryEnum(*I) << ")\n"; 325 OS << "#endif // GET_CATEGORY_TABLE\n\n"; 326} 327 328//===----------------------------------------------------------------------===// 329// Diagnostic name index generation 330//===----------------------------------------------------------------------===// 331 332namespace { 333struct RecordIndexElement 334{ 335 RecordIndexElement() {} 336 explicit RecordIndexElement(Record const &R): 337 Name(R.getName()) {} 338 339 std::string Name; 340}; 341 342struct RecordIndexElementSorter : 343 public std::binary_function<RecordIndexElement, RecordIndexElement, bool> { 344 345 bool operator()(RecordIndexElement const &Lhs, 346 RecordIndexElement const &Rhs) const { 347 return Lhs.Name < Rhs.Name; 348 } 349 350}; 351 352} // end anonymous namespace. 353 354void ClangDiagsIndexNameEmitter::run(raw_ostream &OS) { 355 const std::vector<Record*> &Diags = 356 Records.getAllDerivedDefinitions("Diagnostic"); 357 358 std::vector<RecordIndexElement> Index; 359 Index.reserve(Diags.size()); 360 for (unsigned i = 0, e = Diags.size(); i != e; ++i) { 361 const Record &R = *(Diags[i]); 362 Index.push_back(RecordIndexElement(R)); 363 } 364 365 std::sort(Index.begin(), Index.end(), RecordIndexElementSorter()); 366 367 for (unsigned i = 0, e = Index.size(); i != e; ++i) { 368 const RecordIndexElement &R = Index[i]; 369 370 OS << "DIAG_NAME_INDEX(" << R.Name << ")\n"; 371 } 372} 373