ClangDiagnosticsEmitter.cpp revision 234353
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>
25using namespace llvm;
26
27//===----------------------------------------------------------------------===//
28// Diagnostic category computation code.
29//===----------------------------------------------------------------------===//
30
31namespace {
32class DiagGroupParentMap {
33  RecordKeeper &Records;
34  std::map<const Record*, std::vector<Record*> > Mapping;
35public:
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
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
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.
131static 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
164void 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
250static 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
259void 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
345namespace {
346struct RecordIndexElement
347{
348  RecordIndexElement() {}
349  explicit RecordIndexElement(Record const &R):
350    Name(R.getName()) {}
351
352  std::string Name;
353};
354
355struct 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
367void 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