blob: 6bfb3f9f61f542be94fdee4164d100b87afdfb9c [file] [log] [blame]
Peter Collingbournebee583f2011-10-06 13:03:08 +00001//=- 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
Peter Collingbournebee583f2011-10-06 13:03:08 +000014#include "llvm/ADT/DenseSet.h"
Chandler Carruth59ff16c2012-12-04 09:53:39 +000015#include "llvm/ADT/Optional.h"
16#include "llvm/ADT/PointerUnion.h"
Eric Fiselierb87be182018-05-19 03:12:04 +000017#include "llvm/ADT/STLExtras.h"
Jordan Rosec3b23aa2013-01-10 18:50:46 +000018#include "llvm/ADT/SmallPtrSet.h"
Peter Collingbournebee583f2011-10-06 13:03:08 +000019#include "llvm/ADT/SmallString.h"
Jordan Rosec3b23aa2013-01-10 18:50:46 +000020#include "llvm/ADT/SmallVector.h"
Jakob Stoklund Olesen995e0e12012-06-13 05:12:41 +000021#include "llvm/ADT/StringMap.h"
Jordan Rosec3b23aa2013-01-10 18:50:46 +000022#include "llvm/ADT/Twine.h"
Eric Fiselierb87be182018-05-19 03:12:04 +000023#include "llvm/Support/Casting.h"
Joerg Sonnenberger691a16b2012-10-25 16:37:08 +000024#include "llvm/TableGen/Error.h"
Jakob Stoklund Olesen995e0e12012-06-13 05:12:41 +000025#include "llvm/TableGen/Record.h"
Craig Topperda7cf8a2013-08-29 05:18:04 +000026#include "llvm/TableGen/StringToOffsetTable.h"
Jakob Stoklund Olesen995e0e12012-06-13 05:12:41 +000027#include "llvm/TableGen/TableGenBackend.h"
Peter Collingbournebee583f2011-10-06 13:03:08 +000028#include <algorithm>
Joerg Sonnenberger42cf2682012-08-10 10:58:18 +000029#include <cctype>
Peter Collingbournebee583f2011-10-06 13:03:08 +000030#include <functional>
Jakob Stoklund Olesen995e0e12012-06-13 05:12:41 +000031#include <map>
Richard Smithb6a3b4b2016-09-12 05:58:29 +000032#include <set>
Peter Collingbournebee583f2011-10-06 13:03:08 +000033using namespace llvm;
34
35//===----------------------------------------------------------------------===//
36// Diagnostic category computation code.
37//===----------------------------------------------------------------------===//
38
39namespace {
40class DiagGroupParentMap {
41 RecordKeeper &Records;
42 std::map<const Record*, std::vector<Record*> > Mapping;
43public:
44 DiagGroupParentMap(RecordKeeper &records) : Records(records) {
45 std::vector<Record*> DiagGroups
46 = Records.getAllDerivedDefinitions("DiagGroup");
47 for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) {
48 std::vector<Record*> SubGroups =
49 DiagGroups[i]->getValueAsListOfDefs("SubGroups");
50 for (unsigned j = 0, e = SubGroups.size(); j != e; ++j)
51 Mapping[SubGroups[j]].push_back(DiagGroups[i]);
52 }
53 }
Craig Topperf3932e32013-07-19 21:43:59 +000054
Peter Collingbournebee583f2011-10-06 13:03:08 +000055 const std::vector<Record*> &getParents(const Record *Group) {
56 return Mapping[Group];
57 }
58};
59} // end anonymous namespace.
60
Peter Collingbournebee583f2011-10-06 13:03:08 +000061static std::string
62getCategoryFromDiagGroup(const Record *Group,
63 DiagGroupParentMap &DiagGroupParents) {
64 // If the DiagGroup has a category, return it.
65 std::string CatName = Group->getValueAsString("CategoryName");
66 if (!CatName.empty()) return CatName;
Craig Topperf3932e32013-07-19 21:43:59 +000067
Peter Collingbournebee583f2011-10-06 13:03:08 +000068 // The diag group may the subgroup of one or more other diagnostic groups,
69 // check these for a category as well.
70 const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
71 for (unsigned i = 0, e = Parents.size(); i != e; ++i) {
72 CatName = getCategoryFromDiagGroup(Parents[i], DiagGroupParents);
73 if (!CatName.empty()) return CatName;
74 }
75 return "";
76}
77
78/// getDiagnosticCategory - Return the category that the specified diagnostic
79/// lives in.
80static std::string getDiagnosticCategory(const Record *R,
81 DiagGroupParentMap &DiagGroupParents) {
82 // If the diagnostic is in a group, and that group has a category, use it.
Sean Silva1c4aaa82012-10-10 20:25:43 +000083 if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) {
Peter Collingbournebee583f2011-10-06 13:03:08 +000084 // Check the diagnostic's diag group for a category.
85 std::string CatName = getCategoryFromDiagGroup(Group->getDef(),
86 DiagGroupParents);
87 if (!CatName.empty()) return CatName;
88 }
Ted Kremenekb22ea2a2012-07-07 05:53:30 +000089
Peter Collingbournebee583f2011-10-06 13:03:08 +000090 // If the diagnostic itself has a category, get it.
91 return R->getValueAsString("CategoryName");
92}
93
94namespace {
95 class DiagCategoryIDMap {
96 RecordKeeper &Records;
97 StringMap<unsigned> CategoryIDs;
98 std::vector<std::string> CategoryStrings;
99 public:
100 DiagCategoryIDMap(RecordKeeper &records) : Records(records) {
101 DiagGroupParentMap ParentInfo(Records);
Craig Topperf3932e32013-07-19 21:43:59 +0000102
Peter Collingbournebee583f2011-10-06 13:03:08 +0000103 // The zero'th category is "".
104 CategoryStrings.push_back("");
105 CategoryIDs[""] = 0;
Craig Topperf3932e32013-07-19 21:43:59 +0000106
Peter Collingbournebee583f2011-10-06 13:03:08 +0000107 std::vector<Record*> Diags =
108 Records.getAllDerivedDefinitions("Diagnostic");
109 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
110 std::string Category = getDiagnosticCategory(Diags[i], ParentInfo);
111 if (Category.empty()) continue; // Skip diags with no category.
Craig Topperf3932e32013-07-19 21:43:59 +0000112
Peter Collingbournebee583f2011-10-06 13:03:08 +0000113 unsigned &ID = CategoryIDs[Category];
114 if (ID != 0) continue; // Already seen.
Craig Topperf3932e32013-07-19 21:43:59 +0000115
Peter Collingbournebee583f2011-10-06 13:03:08 +0000116 ID = CategoryStrings.size();
117 CategoryStrings.push_back(Category);
118 }
119 }
Craig Topperf3932e32013-07-19 21:43:59 +0000120
Peter Collingbournebee583f2011-10-06 13:03:08 +0000121 unsigned getID(StringRef CategoryString) {
122 return CategoryIDs[CategoryString];
123 }
Craig Topperf3932e32013-07-19 21:43:59 +0000124
Craig Toppera9bcac52013-07-21 22:20:10 +0000125 typedef std::vector<std::string>::const_iterator const_iterator;
126 const_iterator begin() const { return CategoryStrings.begin(); }
127 const_iterator end() const { return CategoryStrings.end(); }
Peter Collingbournebee583f2011-10-06 13:03:08 +0000128 };
Argyrios Kyrtzidis87acf192012-03-06 00:00:38 +0000129
130 struct GroupInfo {
131 std::vector<const Record*> DiagsInGroup;
132 std::vector<std::string> SubGroups;
133 unsigned IDNo;
Jordan Rosec3b23aa2013-01-10 18:50:46 +0000134
135 const Record *ExplicitDef;
136
Craig Topper8ae12032014-05-07 06:21:57 +0000137 GroupInfo() : ExplicitDef(nullptr) {}
Argyrios Kyrtzidis87acf192012-03-06 00:00:38 +0000138 };
Peter Collingbournebee583f2011-10-06 13:03:08 +0000139} // end anonymous namespace.
140
Jordan Rosec3b23aa2013-01-10 18:50:46 +0000141static bool beforeThanCompare(const Record *LHS, const Record *RHS) {
142 assert(!LHS->getLoc().empty() && !RHS->getLoc().empty());
143 return
144 LHS->getLoc().front().getPointer() < RHS->getLoc().front().getPointer();
145}
146
Richard Smithb6a3b4b2016-09-12 05:58:29 +0000147static bool diagGroupBeforeByName(const Record *LHS, const Record *RHS) {
148 return LHS->getValueAsString("GroupName") <
149 RHS->getValueAsString("GroupName");
150}
151
Jordan Rosec3b23aa2013-01-10 18:50:46 +0000152static bool beforeThanCompareGroups(const GroupInfo *LHS, const GroupInfo *RHS){
153 assert(!LHS->DiagsInGroup.empty() && !RHS->DiagsInGroup.empty());
154 return beforeThanCompare(LHS->DiagsInGroup.front(),
155 RHS->DiagsInGroup.front());
156}
157
Adrian Prantl9fc8faf2018-05-09 01:00:01 +0000158/// Invert the 1-[0/1] mapping of diags to group into a one to many
Argyrios Kyrtzidis87acf192012-03-06 00:00:38 +0000159/// mapping of groups to diags in the group.
160static void groupDiagnostics(const std::vector<Record*> &Diags,
161 const std::vector<Record*> &DiagGroups,
162 std::map<std::string, GroupInfo> &DiagsInGroup) {
Jordan Rosec3b23aa2013-01-10 18:50:46 +0000163
Argyrios Kyrtzidis87acf192012-03-06 00:00:38 +0000164 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
165 const Record *R = Diags[i];
Sean Silva1c4aaa82012-10-10 20:25:43 +0000166 DefInit *DI = dyn_cast<DefInit>(R->getValueInit("Group"));
Craig Topper8ae12032014-05-07 06:21:57 +0000167 if (!DI)
168 continue;
Richard Smithce52ca12012-05-04 19:05:50 +0000169 assert(R->getValueAsDef("Class")->getName() != "CLASS_NOTE" &&
170 "Note can't be in a DiagGroup");
Argyrios Kyrtzidis87acf192012-03-06 00:00:38 +0000171 std::string GroupName = DI->getDef()->getValueAsString("GroupName");
172 DiagsInGroup[GroupName].DiagsInGroup.push_back(R);
173 }
Jordan Rosec3b23aa2013-01-10 18:50:46 +0000174
175 typedef SmallPtrSet<GroupInfo *, 16> GroupSetTy;
176 GroupSetTy ImplicitGroups;
177
Argyrios Kyrtzidis87acf192012-03-06 00:00:38 +0000178 // Add all DiagGroup's to the DiagsInGroup list to make sure we pick up empty
179 // groups (these are warnings that GCC supports that clang never produces).
180 for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) {
181 Record *Group = DiagGroups[i];
182 GroupInfo &GI = DiagsInGroup[Group->getValueAsString("GroupName")];
Jordan Rosec3b23aa2013-01-10 18:50:46 +0000183 if (Group->isAnonymous()) {
184 if (GI.DiagsInGroup.size() > 1)
185 ImplicitGroups.insert(&GI);
186 } else {
187 if (GI.ExplicitDef)
188 assert(GI.ExplicitDef == Group);
189 else
190 GI.ExplicitDef = Group;
191 }
192
Argyrios Kyrtzidis87acf192012-03-06 00:00:38 +0000193 std::vector<Record*> SubGroups = Group->getValueAsListOfDefs("SubGroups");
194 for (unsigned j = 0, e = SubGroups.size(); j != e; ++j)
195 GI.SubGroups.push_back(SubGroups[j]->getValueAsString("GroupName"));
196 }
Craig Topperf3932e32013-07-19 21:43:59 +0000197
Argyrios Kyrtzidis87acf192012-03-06 00:00:38 +0000198 // Assign unique ID numbers to the groups.
199 unsigned IDNo = 0;
200 for (std::map<std::string, GroupInfo>::iterator
201 I = DiagsInGroup.begin(), E = DiagsInGroup.end(); I != E; ++I, ++IDNo)
202 I->second.IDNo = IDNo;
Jordan Rosec3b23aa2013-01-10 18:50:46 +0000203
204 // Sort the implicit groups, so we can warn about them deterministically.
205 SmallVector<GroupInfo *, 16> SortedGroups(ImplicitGroups.begin(),
206 ImplicitGroups.end());
207 for (SmallVectorImpl<GroupInfo *>::iterator I = SortedGroups.begin(),
208 E = SortedGroups.end();
209 I != E; ++I) {
210 MutableArrayRef<const Record *> GroupDiags = (*I)->DiagsInGroup;
Mandeep Singh Grangc205d8c2018-03-27 16:50:00 +0000211 llvm::sort(GroupDiags.begin(), GroupDiags.end(), beforeThanCompare);
Jordan Rosec3b23aa2013-01-10 18:50:46 +0000212 }
Mandeep Singh Grangc205d8c2018-03-27 16:50:00 +0000213 llvm::sort(SortedGroups.begin(), SortedGroups.end(), beforeThanCompareGroups);
Jordan Rosec3b23aa2013-01-10 18:50:46 +0000214
215 // Warn about the same group being used anonymously in multiple places.
216 for (SmallVectorImpl<GroupInfo *>::const_iterator I = SortedGroups.begin(),
217 E = SortedGroups.end();
218 I != E; ++I) {
219 ArrayRef<const Record *> GroupDiags = (*I)->DiagsInGroup;
220
221 if ((*I)->ExplicitDef) {
222 std::string Name = (*I)->ExplicitDef->getValueAsString("GroupName");
223 for (ArrayRef<const Record *>::const_iterator DI = GroupDiags.begin(),
224 DE = GroupDiags.end();
225 DI != DE; ++DI) {
226 const DefInit *GroupInit = cast<DefInit>((*DI)->getValueInit("Group"));
227 const Record *NextDiagGroup = GroupInit->getDef();
228 if (NextDiagGroup == (*I)->ExplicitDef)
229 continue;
230
Nicolai Haehnle846e5782018-03-06 17:55:00 +0000231 SrcMgr.PrintMessage((*DI)->getLoc().front(),
Jordan Rosec3b23aa2013-01-10 18:50:46 +0000232 SourceMgr::DK_Error,
233 Twine("group '") + Name +
Nicolai Haehnle846e5782018-03-06 17:55:00 +0000234 "' is referred to anonymously");
Jordan Rosec3b23aa2013-01-10 18:50:46 +0000235 SrcMgr.PrintMessage((*I)->ExplicitDef->getLoc().front(),
236 SourceMgr::DK_Note, "group defined here");
237 }
238 } else {
239 // If there's no existing named group, we should just warn once and use
240 // notes to list all the other cases.
241 ArrayRef<const Record *>::const_iterator DI = GroupDiags.begin(),
242 DE = GroupDiags.end();
243 assert(DI != DE && "We only care about groups with multiple uses!");
244
245 const DefInit *GroupInit = cast<DefInit>((*DI)->getValueInit("Group"));
246 const Record *NextDiagGroup = GroupInit->getDef();
247 std::string Name = NextDiagGroup->getValueAsString("GroupName");
248
Nicolai Haehnle846e5782018-03-06 17:55:00 +0000249 SrcMgr.PrintMessage((*DI)->getLoc().front(),
Jordan Rosec3b23aa2013-01-10 18:50:46 +0000250 SourceMgr::DK_Error,
251 Twine("group '") + Name +
Nicolai Haehnle846e5782018-03-06 17:55:00 +0000252 "' is referred to anonymously");
Jordan Rosec3b23aa2013-01-10 18:50:46 +0000253
254 for (++DI; DI != DE; ++DI) {
Nicolai Haehnle846e5782018-03-06 17:55:00 +0000255 SrcMgr.PrintMessage((*DI)->getLoc().front(),
256 SourceMgr::DK_Note, "also referenced here");
Jordan Rosec3b23aa2013-01-10 18:50:46 +0000257 }
258 }
259 }
Argyrios Kyrtzidis87acf192012-03-06 00:00:38 +0000260}
Peter Collingbournebee583f2011-10-06 13:03:08 +0000261
262//===----------------------------------------------------------------------===//
Ted Kremenekb22ea2a2012-07-07 05:53:30 +0000263// Infer members of -Wpedantic.
264//===----------------------------------------------------------------------===//
265
266typedef std::vector<const Record *> RecordVec;
267typedef llvm::DenseSet<const Record *> RecordSet;
268typedef llvm::PointerUnion<RecordVec*, RecordSet*> VecOrSet;
269
270namespace {
271class InferPedantic {
272 typedef llvm::DenseMap<const Record*,
Ted Kremenek03325582013-02-21 01:29:01 +0000273 std::pair<unsigned, Optional<unsigned> > > GMap;
Ted Kremenekb22ea2a2012-07-07 05:53:30 +0000274
275 DiagGroupParentMap &DiagGroupParents;
276 const std::vector<Record*> &Diags;
277 const std::vector<Record*> DiagGroups;
278 std::map<std::string, GroupInfo> &DiagsInGroup;
279 llvm::DenseSet<const Record*> DiagsSet;
280 GMap GroupCount;
281public:
282 InferPedantic(DiagGroupParentMap &DiagGroupParents,
283 const std::vector<Record*> &Diags,
284 const std::vector<Record*> &DiagGroups,
285 std::map<std::string, GroupInfo> &DiagsInGroup)
286 : DiagGroupParents(DiagGroupParents),
287 Diags(Diags),
288 DiagGroups(DiagGroups),
289 DiagsInGroup(DiagsInGroup) {}
290
291 /// Compute the set of diagnostics and groups that are immediately
292 /// in -Wpedantic.
293 void compute(VecOrSet DiagsInPedantic,
294 VecOrSet GroupsInPedantic);
295
296private:
297 /// Determine whether a group is a subgroup of another group.
298 bool isSubGroupOfGroup(const Record *Group,
299 llvm::StringRef RootGroupName);
300
301 /// Determine if the diagnostic is an extension.
302 bool isExtension(const Record *Diag);
303
Ted Kremenek5d858cb2012-08-10 20:50:00 +0000304 /// Determine if the diagnostic is off by default.
305 bool isOffByDefault(const Record *Diag);
306
Ted Kremenekb22ea2a2012-07-07 05:53:30 +0000307 /// Increment the count for a group, and transitively marked
308 /// parent groups when appropriate.
309 void markGroup(const Record *Group);
310
311 /// Return true if the diagnostic is in a pedantic group.
312 bool groupInPedantic(const Record *Group, bool increment = false);
313};
314} // end anonymous namespace
315
316bool InferPedantic::isSubGroupOfGroup(const Record *Group,
317 llvm::StringRef GName) {
318
319 const std::string &GroupName = Group->getValueAsString("GroupName");
320 if (GName == GroupName)
321 return true;
322
323 const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
324 for (unsigned i = 0, e = Parents.size(); i != e; ++i)
325 if (isSubGroupOfGroup(Parents[i], GName))
326 return true;
327
328 return false;
329}
330
331/// Determine if the diagnostic is an extension.
332bool InferPedantic::isExtension(const Record *Diag) {
333 const std::string &ClsName = Diag->getValueAsDef("Class")->getName();
334 return ClsName == "CLASS_EXTENSION";
335}
336
Ted Kremenek5d858cb2012-08-10 20:50:00 +0000337bool InferPedantic::isOffByDefault(const Record *Diag) {
Alp Toker46df1c02014-06-12 10:15:20 +0000338 const std::string &DefSeverity =
339 Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name");
340 return DefSeverity == "Ignored";
Ted Kremenek5d858cb2012-08-10 20:50:00 +0000341}
342
Ted Kremenekb22ea2a2012-07-07 05:53:30 +0000343bool InferPedantic::groupInPedantic(const Record *Group, bool increment) {
344 GMap::mapped_type &V = GroupCount[Group];
345 // Lazily compute the threshold value for the group count.
346 if (!V.second.hasValue()) {
347 const GroupInfo &GI = DiagsInGroup[Group->getValueAsString("GroupName")];
348 V.second = GI.SubGroups.size() + GI.DiagsInGroup.size();
349 }
350
351 if (increment)
352 ++V.first;
353
354 // Consider a group in -Wpendatic IFF if has at least one diagnostic
355 // or subgroup AND all of those diagnostics and subgroups are covered
356 // by -Wpedantic via our computation.
357 return V.first != 0 && V.first == V.second.getValue();
358}
359
360void InferPedantic::markGroup(const Record *Group) {
361 // If all the diagnostics and subgroups have been marked as being
362 // covered by -Wpedantic, increment the count of parent groups. Once the
363 // group's count is equal to the number of subgroups and diagnostics in
364 // that group, we can safely add this group to -Wpedantic.
365 if (groupInPedantic(Group, /* increment */ true)) {
366 const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
367 for (unsigned i = 0, e = Parents.size(); i != e; ++i)
368 markGroup(Parents[i]);
369 }
370}
371
372void InferPedantic::compute(VecOrSet DiagsInPedantic,
373 VecOrSet GroupsInPedantic) {
Ted Kremenek5d858cb2012-08-10 20:50:00 +0000374 // All extensions that are not on by default are implicitly in the
375 // "pedantic" group. For those that aren't explicitly included in -Wpedantic,
376 // mark them for consideration to be included in -Wpedantic directly.
Ted Kremenekb22ea2a2012-07-07 05:53:30 +0000377 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
378 Record *R = Diags[i];
Ted Kremenek5d858cb2012-08-10 20:50:00 +0000379 if (isExtension(R) && isOffByDefault(R)) {
Ted Kremenekb22ea2a2012-07-07 05:53:30 +0000380 DiagsSet.insert(R);
Sean Silva1c4aaa82012-10-10 20:25:43 +0000381 if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) {
Ted Kremenekb22ea2a2012-07-07 05:53:30 +0000382 const Record *GroupRec = Group->getDef();
383 if (!isSubGroupOfGroup(GroupRec, "pedantic")) {
384 markGroup(GroupRec);
385 }
386 }
387 }
388 }
389
390 // Compute the set of diagnostics that are directly in -Wpedantic. We
391 // march through Diags a second time to ensure the results are emitted
392 // in deterministic order.
393 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
394 Record *R = Diags[i];
395 if (!DiagsSet.count(R))
396 continue;
397 // Check if the group is implicitly in -Wpedantic. If so,
398 // the diagnostic should not be directly included in the -Wpedantic
399 // diagnostic group.
Sean Silva1c4aaa82012-10-10 20:25:43 +0000400 if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group")))
Ted Kremenekb22ea2a2012-07-07 05:53:30 +0000401 if (groupInPedantic(Group->getDef()))
402 continue;
403
404 // The diagnostic is not included in a group that is (transitively) in
405 // -Wpedantic. Include it in -Wpedantic directly.
406 if (RecordVec *V = DiagsInPedantic.dyn_cast<RecordVec*>())
407 V->push_back(R);
408 else {
409 DiagsInPedantic.get<RecordSet*>()->insert(R);
410 }
411 }
412
413 if (!GroupsInPedantic)
414 return;
415
416 // Compute the set of groups that are directly in -Wpedantic. We
417 // march through the groups to ensure the results are emitted
418 /// in a deterministc order.
419 for (unsigned i = 0, ei = DiagGroups.size(); i != ei; ++i) {
420 Record *Group = DiagGroups[i];
421 if (!groupInPedantic(Group))
422 continue;
423
424 unsigned ParentsInPedantic = 0;
425 const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
426 for (unsigned j = 0, ej = Parents.size(); j != ej; ++j) {
427 if (groupInPedantic(Parents[j]))
428 ++ParentsInPedantic;
429 }
430 // If all the parents are in -Wpedantic, this means that this diagnostic
431 // group will be indirectly included by -Wpedantic already. In that
432 // case, do not add it directly to -Wpedantic. If the group has no
433 // parents, obviously it should go into -Wpedantic.
434 if (Parents.size() > 0 && ParentsInPedantic == Parents.size())
435 continue;
436
437 if (RecordVec *V = GroupsInPedantic.dyn_cast<RecordVec*>())
438 V->push_back(Group);
439 else {
440 GroupsInPedantic.get<RecordSet*>()->insert(Group);
441 }
442 }
443}
444
Eric Fiselierb87be182018-05-19 03:12:04 +0000445namespace {
446enum PieceKind {
447 MultiPieceClass,
448 TextPieceClass,
449 PlaceholderPieceClass,
450 SelectPieceClass,
451 PluralPieceClass,
452 DiffPieceClass,
453 SubstitutionPieceClass,
454};
455
456enum ModifierType {
457 MT_Unknown,
458 MT_Placeholder,
459 MT_Select,
460 MT_Sub,
461 MT_Plural,
462 MT_Diff,
463 MT_Ordinal,
464 MT_S,
465 MT_Q,
466 MT_ObjCClass,
467 MT_ObjCInstance,
468};
469
470static StringRef getModifierName(ModifierType MT) {
471 switch (MT) {
472 case MT_Select:
473 return "select";
474 case MT_Sub:
475 return "sub";
476 case MT_Diff:
477 return "diff";
478 case MT_Plural:
479 return "plural";
480 case MT_Ordinal:
481 return "ordinal";
482 case MT_S:
483 return "s";
484 case MT_Q:
485 return "q";
486 case MT_Placeholder:
487 return "";
488 case MT_ObjCClass:
489 return "objcclass";
490 case MT_ObjCInstance:
491 return "objcinstance";
492 case MT_Unknown:
493 llvm_unreachable("invalid modifier type");
494 }
Mark Searlesbcf611a2018-05-21 17:29:08 +0000495 // Unhandled case
496 llvm_unreachable("invalid modifier type");
Eric Fiselierb87be182018-05-19 03:12:04 +0000497}
498
499struct Piece {
500 // This type and its derived classes are move-only.
501 Piece(PieceKind Kind) : ClassKind(Kind) {}
502 Piece(Piece const &O) = delete;
503 Piece &operator=(Piece const &) = delete;
504 virtual ~Piece() {}
505
506 PieceKind getPieceClass() const { return ClassKind; }
507 static bool classof(const Piece *) { return true; }
508
509private:
510 PieceKind ClassKind;
511};
512
513struct MultiPiece : Piece {
514 MultiPiece() : Piece(MultiPieceClass) {}
515 MultiPiece(std::vector<Piece *> Pieces)
516 : Piece(MultiPieceClass), Pieces(std::move(Pieces)) {}
517
518 std::vector<Piece *> Pieces;
519
520 static bool classof(const Piece *P) {
521 return P->getPieceClass() == MultiPieceClass;
522 }
523};
524
525struct TextPiece : Piece {
526 StringRef Role;
527 std::string Text;
528 TextPiece(StringRef Text, StringRef Role = "")
529 : Piece(TextPieceClass), Role(Role), Text(Text.str()) {}
530
531 static bool classof(const Piece *P) {
532 return P->getPieceClass() == TextPieceClass;
533 }
534};
535
536struct PlaceholderPiece : Piece {
537 ModifierType Kind;
538 int Index;
539 PlaceholderPiece(ModifierType Kind, int Index)
540 : Piece(PlaceholderPieceClass), Kind(Kind), Index(Index) {}
541
542 static bool classof(const Piece *P) {
543 return P->getPieceClass() == PlaceholderPieceClass;
544 }
545};
546
547struct SelectPiece : Piece {
548protected:
549 SelectPiece(PieceKind Kind, ModifierType ModKind)
550 : Piece(Kind), ModKind(ModKind) {}
551
552public:
553 SelectPiece(ModifierType ModKind) : SelectPiece(SelectPieceClass, ModKind) {}
554
555 ModifierType ModKind;
556 std::vector<Piece *> Options;
557 int Index;
558
559 static bool classof(const Piece *P) {
560 return P->getPieceClass() == SelectPieceClass ||
561 P->getPieceClass() == PluralPieceClass;
562 }
563};
564
565struct PluralPiece : SelectPiece {
566 PluralPiece() : SelectPiece(PluralPieceClass, MT_Plural) {}
567
568 std::vector<Piece *> OptionPrefixes;
569 int Index;
570
571 static bool classof(const Piece *P) {
572 return P->getPieceClass() == PluralPieceClass;
573 }
574};
575
576struct DiffPiece : Piece {
577 DiffPiece() : Piece(DiffPieceClass) {}
578
579 Piece *Options[2] = {};
580 int Indexes[2] = {};
581
582 static bool classof(const Piece *P) {
583 return P->getPieceClass() == DiffPieceClass;
584 }
585};
586
587struct SubstitutionPiece : Piece {
588 SubstitutionPiece() : Piece(SubstitutionPieceClass) {}
589
590 std::string Name;
591 std::vector<int> Modifiers;
592
593 static bool classof(const Piece *P) {
594 return P->getPieceClass() == SubstitutionPieceClass;
595 }
596};
597
598/// Diagnostic text, parsed into pieces.
599
600
601struct DiagnosticTextBuilder {
602 DiagnosticTextBuilder(DiagnosticTextBuilder const &) = delete;
603 DiagnosticTextBuilder &operator=(DiagnosticTextBuilder const &) = delete;
604
605 DiagnosticTextBuilder(RecordKeeper &Records) {
606 // Build up the list of substitution records.
607 for (auto *S : Records.getAllDerivedDefinitions("TextSubstitution")) {
608 EvaluatingRecordGuard Guard(&EvaluatingRecord, S);
609 Substitutions.try_emplace(
610 S->getName(), DiagText(*this, S->getValueAsString("Substitution")));
611 }
612
613 // Check that no diagnostic definitions have the same name as a
614 // substitution.
615 for (Record *Diag : Records.getAllDerivedDefinitions("Diagnostic")) {
616 StringRef Name = Diag->getName();
617 if (Substitutions.count(Name))
618 llvm::PrintFatalError(
619 Diag->getLoc(),
620 "Diagnostic '" + Name +
621 "' has same name as TextSubstitution definition");
622 }
623 }
624
625 std::vector<std::string> buildForDocumentation(StringRef Role,
626 const Record *R);
627 std::string buildForDefinition(const Record *R);
628
629 Piece *getSubstitution(SubstitutionPiece *S) const {
630 auto It = Substitutions.find(S->Name);
631 if (It == Substitutions.end())
632 PrintFatalError("Failed to find substitution with name: " + S->Name);
633 return It->second.Root;
634 }
635
David Bolvansky05a9bc12018-05-26 09:24:00 +0000636 LLVM_ATTRIBUTE_NORETURN void PrintFatalError(llvm::Twine const &Msg) const {
Eric Fiselierb87be182018-05-19 03:12:04 +0000637 assert(EvaluatingRecord && "not evaluating a record?");
638 llvm::PrintFatalError(EvaluatingRecord->getLoc(), Msg);
639 }
640
641private:
642 struct DiagText {
643 DiagnosticTextBuilder &Builder;
644 std::vector<Piece *> AllocatedPieces;
645 Piece *Root = nullptr;
646
647 template <class T, class... Args> T *New(Args &&... args) {
648 static_assert(std::is_base_of<Piece, T>::value, "must be piece");
649 T *Mem = new T(std::forward<Args>(args)...);
650 AllocatedPieces.push_back(Mem);
651 return Mem;
652 }
653
654 DiagText(DiagnosticTextBuilder &Builder, StringRef Text)
655 : Builder(Builder), Root(parseDiagText(Text)) {}
656
657 Piece *parseDiagText(StringRef &Text, bool Nested = false);
658 int parseModifier(StringRef &) const;
659
660 public:
661 DiagText(DiagText &&O) noexcept
662 : Builder(O.Builder), AllocatedPieces(std::move(O.AllocatedPieces)),
663 Root(O.Root) {
664 O.Root = nullptr;
665 }
666
667 ~DiagText() {
668 for (Piece *P : AllocatedPieces)
669 delete P;
670 }
671 };
672
673private:
674 const Record *EvaluatingRecord = nullptr;
675 struct EvaluatingRecordGuard {
676 EvaluatingRecordGuard(const Record **Dest, const Record *New)
677 : Dest(Dest), Old(*Dest) {
678 *Dest = New;
679 }
680 ~EvaluatingRecordGuard() { *Dest = Old; }
681 const Record **Dest;
682 const Record *Old;
683 };
684
685 StringMap<DiagText> Substitutions;
686};
687
688template <class Derived> struct DiagTextVisitor {
689 using ModifierMappingsType = Optional<std::vector<int>>;
690
691private:
692 Derived &getDerived() { return static_cast<Derived &>(*this); }
693
694public:
695 std::vector<int>
696 getSubstitutionMappings(SubstitutionPiece *P,
697 const ModifierMappingsType &Mappings) const {
698 std::vector<int> NewMappings;
699 for (int Idx : P->Modifiers)
700 NewMappings.push_back(mapIndex(Idx, Mappings));
701 return NewMappings;
702 }
703
704 struct SubstitutionContext {
705 SubstitutionContext(DiagTextVisitor &Visitor, SubstitutionPiece *P)
706 : Visitor(Visitor) {
707 Substitution = Visitor.Builder.getSubstitution(P);
708 OldMappings = std::move(Visitor.ModifierMappings);
709 std::vector<int> NewMappings =
710 Visitor.getSubstitutionMappings(P, OldMappings);
711 Visitor.ModifierMappings = std::move(NewMappings);
712 }
713
714 ~SubstitutionContext() {
715 Visitor.ModifierMappings = std::move(OldMappings);
716 }
717
718 private:
719 DiagTextVisitor &Visitor;
720 Optional<std::vector<int>> OldMappings;
721
722 public:
723 Piece *Substitution;
724 };
725
726public:
727 DiagTextVisitor(DiagnosticTextBuilder &Builder) : Builder(Builder) {}
728
729 void Visit(Piece *P) {
730 switch (P->getPieceClass()) {
731#define CASE(T) \
732 case T##PieceClass: \
733 return getDerived().Visit##T(static_cast<T##Piece *>(P))
734 CASE(Multi);
735 CASE(Text);
736 CASE(Placeholder);
737 CASE(Select);
738 CASE(Plural);
739 CASE(Diff);
740 CASE(Substitution);
741#undef CASE
742 }
743 }
744
745 void VisitSubstitution(SubstitutionPiece *P) {
746 SubstitutionContext Guard(*this, P);
747 Visit(Guard.Substitution);
748 }
749
750 int mapIndex(int Idx,
751 ModifierMappingsType const &ModifierMappings) const {
752 if (!ModifierMappings)
753 return Idx;
754 if (ModifierMappings->size() <= static_cast<unsigned>(Idx))
755 Builder.PrintFatalError("Modifier value '" + std::to_string(Idx) +
756 "' is not valid for this mapping (has " +
757 std::to_string(ModifierMappings->size()) +
758 " mappings)");
759 return (*ModifierMappings)[Idx];
760 }
761
762 int mapIndex(int Idx) const {
763 return mapIndex(Idx, ModifierMappings);
764 }
765
766protected:
767 DiagnosticTextBuilder &Builder;
768 ModifierMappingsType ModifierMappings;
769};
770
771void escapeRST(StringRef Str, std::string &Out) {
772 for (auto K : Str) {
773 if (StringRef("`*|_[]\\").count(K))
774 Out.push_back('\\');
775 Out.push_back(K);
776 }
777}
778
779template <typename It> void padToSameLength(It Begin, It End) {
780 size_t Width = 0;
781 for (It I = Begin; I != End; ++I)
782 Width = std::max(Width, I->size());
783 for (It I = Begin; I != End; ++I)
784 (*I) += std::string(Width - I->size(), ' ');
785}
786
787template <typename It> void makeTableRows(It Begin, It End) {
788 if (Begin == End)
789 return;
790 padToSameLength(Begin, End);
791 for (It I = Begin; I != End; ++I)
792 *I = "|" + *I + "|";
793}
794
795void makeRowSeparator(std::string &Str) {
796 for (char &K : Str)
797 K = (K == '|' ? '+' : '-');
798}
799
800struct DiagTextDocPrinter : DiagTextVisitor<DiagTextDocPrinter> {
801 using BaseTy = DiagTextVisitor<DiagTextDocPrinter>;
802 DiagTextDocPrinter(DiagnosticTextBuilder &Builder,
803 std::vector<std::string> &RST)
804 : BaseTy(Builder), RST(RST) {}
805
806 void gatherNodes(
807 Piece *OrigP, const ModifierMappingsType &CurrentMappings,
808 std::vector<std::pair<Piece *, ModifierMappingsType>> &Pieces) const {
809 if (auto *Sub = dyn_cast<SubstitutionPiece>(OrigP)) {
810 ModifierMappingsType NewMappings =
811 getSubstitutionMappings(Sub, CurrentMappings);
812 return gatherNodes(Builder.getSubstitution(Sub), NewMappings, Pieces);
813 }
814 if (auto *MD = dyn_cast<MultiPiece>(OrigP)) {
815 for (Piece *Node : MD->Pieces)
816 gatherNodes(Node, CurrentMappings, Pieces);
817 return;
818 }
819 Pieces.push_back(std::make_pair(OrigP, CurrentMappings));
820 }
821
822 void VisitMulti(MultiPiece *P) {
823 if (P->Pieces.empty()) {
824 RST.push_back("");
825 return;
826 }
827
828 if (P->Pieces.size() == 1)
829 return Visit(P->Pieces[0]);
830
831 // Flatten the list of nodes, replacing any substitution pieces with the
832 // recursively flattened substituted node.
833 std::vector<std::pair<Piece *, ModifierMappingsType>> Pieces;
834 gatherNodes(P, ModifierMappings, Pieces);
835
836 std::string EmptyLinePrefix;
837 size_t Start = RST.size();
838 bool HasMultipleLines = true;
839 for (const std::pair<Piece *, ModifierMappingsType> &NodePair : Pieces) {
840 std::vector<std::string> Lines;
841 DiagTextDocPrinter Visitor{Builder, Lines};
842 Visitor.ModifierMappings = NodePair.second;
843 Visitor.Visit(NodePair.first);
844
845 if (Lines.empty())
846 continue;
847
848 // We need a vertical separator if either this or the previous piece is a
849 // multi-line piece, or this is the last piece.
850 const char *Separator = (Lines.size() > 1 || HasMultipleLines) ? "|" : "";
851 HasMultipleLines = Lines.size() > 1;
852
853 if (Start + Lines.size() > RST.size())
854 RST.resize(Start + Lines.size(), EmptyLinePrefix);
855
856 padToSameLength(Lines.begin(), Lines.end());
857 for (size_t I = 0; I != Lines.size(); ++I)
858 RST[Start + I] += Separator + Lines[I];
859 std::string Empty(Lines[0].size(), ' ');
860 for (size_t I = Start + Lines.size(); I != RST.size(); ++I)
861 RST[I] += Separator + Empty;
862 EmptyLinePrefix += Separator + Empty;
863 }
864 for (size_t I = Start; I != RST.size(); ++I)
865 RST[I] += "|";
866 EmptyLinePrefix += "|";
867
868 makeRowSeparator(EmptyLinePrefix);
869 RST.insert(RST.begin() + Start, EmptyLinePrefix);
870 RST.insert(RST.end(), EmptyLinePrefix);
871 }
872
873 void VisitText(TextPiece *P) {
874 RST.push_back("");
875 auto &S = RST.back();
876
877 StringRef T = P->Text;
878 while (!T.empty() && T.front() == ' ') {
879 RST.back() += " |nbsp| ";
880 T = T.drop_front();
881 }
882
883 std::string Suffix;
884 while (!T.empty() && T.back() == ' ') {
885 Suffix += " |nbsp| ";
886 T = T.drop_back();
887 }
888
889 if (!T.empty()) {
890 S += ':';
891 S += P->Role;
892 S += ":`";
893 escapeRST(T, S);
894 S += '`';
895 }
896
897 S += Suffix;
898 }
899
900 void VisitPlaceholder(PlaceholderPiece *P) {
901 RST.push_back(std::string(":placeholder:`") +
902 char('A' + mapIndex(P->Index)) + "`");
903 }
904
905 void VisitSelect(SelectPiece *P) {
906 std::vector<size_t> SeparatorIndexes;
907 SeparatorIndexes.push_back(RST.size());
908 RST.emplace_back();
909 for (auto *O : P->Options) {
910 Visit(O);
911 SeparatorIndexes.push_back(RST.size());
912 RST.emplace_back();
913 }
914
915 makeTableRows(RST.begin() + SeparatorIndexes.front(),
916 RST.begin() + SeparatorIndexes.back() + 1);
917 for (size_t I : SeparatorIndexes)
918 makeRowSeparator(RST[I]);
919 }
920
921 void VisitPlural(PluralPiece *P) { VisitSelect(P); }
922
923 void VisitDiff(DiffPiece *P) { Visit(P->Options[1]); }
924
925 std::vector<std::string> &RST;
926};
927
928struct DiagTextPrinter : DiagTextVisitor<DiagTextPrinter> {
929public:
930 using BaseTy = DiagTextVisitor<DiagTextPrinter>;
931 DiagTextPrinter(DiagnosticTextBuilder &Builder, std::string &Result)
932 : BaseTy(Builder), Result(Result) {}
933
934 void VisitMulti(MultiPiece *P) {
935 for (auto *Child : P->Pieces)
936 Visit(Child);
937 }
938 void VisitText(TextPiece *P) { Result += P->Text; }
939 void VisitPlaceholder(PlaceholderPiece *P) {
940 Result += "%";
941 Result += getModifierName(P->Kind);
942 addInt(mapIndex(P->Index));
943 }
944 void VisitSelect(SelectPiece *P) {
945 Result += "%";
946 Result += getModifierName(P->ModKind);
947 if (P->ModKind == MT_Select) {
948 Result += "{";
949 for (auto *D : P->Options) {
950 Visit(D);
951 Result += '|';
952 }
953 if (!P->Options.empty())
954 Result.erase(--Result.end());
955 Result += '}';
956 }
957 addInt(mapIndex(P->Index));
958 }
959
960 void VisitPlural(PluralPiece *P) {
961 Result += "%plural{";
962 assert(P->Options.size() == P->OptionPrefixes.size());
963 for (unsigned I = 0, End = P->Options.size(); I < End; ++I) {
964 if (P->OptionPrefixes[I])
965 Visit(P->OptionPrefixes[I]);
966 Visit(P->Options[I]);
967 Result += "|";
968 }
969 if (!P->Options.empty())
970 Result.erase(--Result.end());
971 Result += '}';
972 addInt(mapIndex(P->Index));
973 }
974
975 void VisitDiff(DiffPiece *P) {
976 Result += "%diff{";
977 Visit(P->Options[0]);
978 Result += "|";
979 Visit(P->Options[1]);
980 Result += "}";
981 addInt(mapIndex(P->Indexes[0]));
982 Result += ",";
983 addInt(mapIndex(P->Indexes[1]));
984 }
985
986 void addInt(int Val) { Result += std::to_string(Val); }
987
988 std::string &Result;
989};
990
991int DiagnosticTextBuilder::DiagText::parseModifier(StringRef &Text) const {
992 if (Text.empty() || !isdigit(Text[0]))
993 Builder.PrintFatalError("expected modifier in diagnostic");
994 int Val = 0;
995 do {
996 Val *= 10;
997 Val += Text[0] - '0';
998 Text = Text.drop_front();
999 } while (!Text.empty() && isdigit(Text[0]));
1000 return Val;
1001}
1002
1003Piece *DiagnosticTextBuilder::DiagText::parseDiagText(StringRef &Text,
1004 bool Nested) {
1005 std::vector<Piece *> Parsed;
1006
1007 while (!Text.empty()) {
1008 size_t End = (size_t)-2;
1009 do
1010 End = Nested ? Text.find_first_of("%|}", End + 2)
1011 : Text.find_first_of('%', End + 2);
1012 while (End < Text.size() - 1 && Text[End] == '%' &&
1013 (Text[End + 1] == '%' || Text[End + 1] == '|'));
1014
1015 if (End) {
1016 Parsed.push_back(New<TextPiece>(Text.slice(0, End), "diagtext"));
1017 Text = Text.slice(End, StringRef::npos);
1018 if (Text.empty())
1019 break;
1020 }
1021
1022 if (Text[0] == '|' || Text[0] == '}')
1023 break;
1024
1025 // Drop the '%'.
1026 Text = Text.drop_front();
1027
1028 // Extract the (optional) modifier.
1029 size_t ModLength = Text.find_first_of("0123456789{");
1030 StringRef Modifier = Text.slice(0, ModLength);
1031 Text = Text.slice(ModLength, StringRef::npos);
1032 ModifierType ModType = llvm::StringSwitch<ModifierType>{Modifier}
1033 .Case("select", MT_Select)
1034 .Case("sub", MT_Sub)
1035 .Case("diff", MT_Diff)
1036 .Case("plural", MT_Plural)
1037 .Case("s", MT_S)
1038 .Case("ordinal", MT_Ordinal)
1039 .Case("q", MT_Q)
1040 .Case("objcclass", MT_ObjCClass)
1041 .Case("objcinstance", MT_ObjCInstance)
1042 .Case("", MT_Placeholder)
1043 .Default(MT_Unknown);
1044
1045 switch (ModType) {
1046 case MT_Unknown:
1047 Builder.PrintFatalError("Unknown modifier type: " + Modifier);
1048 case MT_Select: {
1049 SelectPiece *Select = New<SelectPiece>(MT_Select);
1050 do {
1051 Text = Text.drop_front(); // '{' or '|'
1052 Select->Options.push_back(parseDiagText(Text, true));
1053 assert(!Text.empty() && "malformed %select");
1054 } while (Text.front() == '|');
1055 // Drop the trailing '}'.
1056 Text = Text.drop_front(1);
1057 Select->Index = parseModifier(Text);
1058 Parsed.push_back(Select);
1059 continue;
1060 }
1061 case MT_Plural: {
1062 PluralPiece *Plural = New<PluralPiece>();
1063 do {
1064 Text = Text.drop_front(); // '{' or '|'
1065 size_t End = Text.find_first_of(":");
1066 if (End == StringRef::npos)
1067 Builder.PrintFatalError("expected ':' while parsing %plural");
1068 ++End;
1069 assert(!Text.empty());
1070 Plural->OptionPrefixes.push_back(
1071 New<TextPiece>(Text.slice(0, End), "diagtext"));
1072 Text = Text.slice(End, StringRef::npos);
1073 Plural->Options.push_back(parseDiagText(Text, true));
1074 assert(!Text.empty() && "malformed %select");
1075 } while (Text.front() == '|');
1076 // Drop the trailing '}'.
1077 Text = Text.drop_front(1);
1078 Plural->Index = parseModifier(Text);
1079 Parsed.push_back(Plural);
1080 continue;
1081 }
1082 case MT_Sub: {
1083 SubstitutionPiece *Sub = New<SubstitutionPiece>();
1084 Text = Text.drop_front(); // '{'
1085 size_t NameSize = Text.find_first_of('}');
1086 assert(NameSize != size_t(-1) && "failed to find the end of the name");
1087 assert(NameSize != 0 && "empty name?");
1088 Sub->Name = Text.substr(0, NameSize).str();
1089 Text = Text.drop_front(NameSize);
1090 Text = Text.drop_front(); // '}'
1091 if (!Text.empty()) {
1092 while (true) {
1093 if (!isdigit(Text[0]))
1094 break;
1095 Sub->Modifiers.push_back(parseModifier(Text));
1096 if (Text.empty() || Text[0] != ',')
1097 break;
1098 Text = Text.drop_front(); // ','
1099 assert(!Text.empty() && isdigit(Text[0]) &&
1100 "expected another modifier");
1101 }
1102 }
1103 Parsed.push_back(Sub);
1104 continue;
1105 }
1106 case MT_Diff: {
1107 DiffPiece *Diff = New<DiffPiece>();
1108 Text = Text.drop_front(); // '{'
1109 Diff->Options[0] = parseDiagText(Text, true);
1110 Text = Text.drop_front(); // '|'
1111 Diff->Options[1] = parseDiagText(Text, true);
1112
1113 Text = Text.drop_front(); // '}'
1114 Diff->Indexes[0] = parseModifier(Text);
1115 Text = Text.drop_front(); // ','
1116 Diff->Indexes[1] = parseModifier(Text);
1117 Parsed.push_back(Diff);
1118 continue;
1119 }
1120 case MT_S: {
1121 SelectPiece *Select = New<SelectPiece>(ModType);
1122 Select->Options.push_back(New<TextPiece>(""));
1123 Select->Options.push_back(New<TextPiece>("s", "diagtext"));
1124 Select->Index = parseModifier(Text);
1125 Parsed.push_back(Select);
1126 continue;
1127 }
1128 case MT_Q:
1129 case MT_Placeholder:
1130 case MT_ObjCClass:
1131 case MT_ObjCInstance:
1132 case MT_Ordinal: {
1133 Parsed.push_back(New<PlaceholderPiece>(ModType, parseModifier(Text)));
1134 continue;
1135 }
1136 }
1137 }
1138
1139 return New<MultiPiece>(Parsed);
1140}
1141
1142std::vector<std::string>
1143DiagnosticTextBuilder::buildForDocumentation(StringRef Severity,
1144 const Record *R) {
1145 EvaluatingRecordGuard Guard(&EvaluatingRecord, R);
1146 StringRef Text = R->getValueAsString("Text");
1147
1148 DiagText D(*this, Text);
1149 TextPiece *Prefix = D.New<TextPiece>(Severity, Severity);
1150 Prefix->Text += ": ";
1151 auto *MP = dyn_cast<MultiPiece>(D.Root);
1152 if (!MP) {
1153 MP = D.New<MultiPiece>();
1154 MP->Pieces.push_back(D.Root);
1155 D.Root = MP;
1156 }
1157 MP->Pieces.insert(MP->Pieces.begin(), Prefix);
1158 std::vector<std::string> Result;
1159 DiagTextDocPrinter{*this, Result}.Visit(D.Root);
1160 return Result;
1161}
1162
1163std::string DiagnosticTextBuilder::buildForDefinition(const Record *R) {
1164 EvaluatingRecordGuard Guard(&EvaluatingRecord, R);
1165 StringRef Text = R->getValueAsString("Text");
1166 DiagText D(*this, Text);
1167 std::string Result;
1168 DiagTextPrinter{*this, Result}.Visit(D.Root);
1169 return Result;
1170}
1171
1172} // namespace
1173
Ted Kremenekb22ea2a2012-07-07 05:53:30 +00001174//===----------------------------------------------------------------------===//
Peter Collingbournebee583f2011-10-06 13:03:08 +00001175// Warning Tables (.inc file) generation.
1176//===----------------------------------------------------------------------===//
1177
Ted Kremenek72492392012-08-07 05:01:49 +00001178static bool isError(const Record &Diag) {
1179 const std::string &ClsName = Diag.getValueAsDef("Class")->getName();
1180 return ClsName == "CLASS_ERROR";
1181}
1182
Tobias Grosser74160242014-02-28 09:11:08 +00001183static bool isRemark(const Record &Diag) {
1184 const std::string &ClsName = Diag.getValueAsDef("Class")->getName();
1185 return ClsName == "CLASS_REMARK";
1186}
1187
Eric Fiselierb87be182018-05-19 03:12:04 +00001188
Jakob Stoklund Olesen995e0e12012-06-13 05:12:41 +00001189/// ClangDiagsDefsEmitter - The top-level class emits .def files containing
1190/// declarations of Clang diagnostics.
1191namespace clang {
1192void EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS,
1193 const std::string &Component) {
Peter Collingbournebee583f2011-10-06 13:03:08 +00001194 // Write the #if guard
1195 if (!Component.empty()) {
Benjamin Kramer44f91da2011-11-06 20:36:48 +00001196 std::string ComponentName = StringRef(Component).upper();
Peter Collingbournebee583f2011-10-06 13:03:08 +00001197 OS << "#ifdef " << ComponentName << "START\n";
1198 OS << "__" << ComponentName << "START = DIAG_START_" << ComponentName
1199 << ",\n";
1200 OS << "#undef " << ComponentName << "START\n";
1201 OS << "#endif\n\n";
1202 }
1203
Eric Fiselierb87be182018-05-19 03:12:04 +00001204 DiagnosticTextBuilder DiagTextBuilder(Records);
1205
1206 std::vector<Record *> Diags = Records.getAllDerivedDefinitions("Diagnostic");
Benjamin Kramera8cafe22012-02-15 20:57:03 +00001207
Benjamin Kramera8cafe22012-02-15 20:57:03 +00001208 std::vector<Record*> DiagGroups
1209 = Records.getAllDerivedDefinitions("DiagGroup");
Argyrios Kyrtzidis87acf192012-03-06 00:00:38 +00001210
1211 std::map<std::string, GroupInfo> DiagsInGroup;
1212 groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
Benjamin Kramera8cafe22012-02-15 20:57:03 +00001213
Peter Collingbournebee583f2011-10-06 13:03:08 +00001214 DiagCategoryIDMap CategoryIDs(Records);
1215 DiagGroupParentMap DGParentMap(Records);
1216
Ted Kremenekb22ea2a2012-07-07 05:53:30 +00001217 // Compute the set of diagnostics that are in -Wpedantic.
1218 RecordSet DiagsInPedantic;
1219 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
Craig Topper8ae12032014-05-07 06:21:57 +00001220 inferPedantic.compute(&DiagsInPedantic, (RecordVec*)nullptr);
Ted Kremenekb22ea2a2012-07-07 05:53:30 +00001221
Peter Collingbournebee583f2011-10-06 13:03:08 +00001222 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
1223 const Record &R = *Diags[i];
Craig Topperf3932e32013-07-19 21:43:59 +00001224
Ted Kremenek72492392012-08-07 05:01:49 +00001225 // Check if this is an error that is accidentally in a warning
1226 // group.
1227 if (isError(R)) {
Sean Silva1c4aaa82012-10-10 20:25:43 +00001228 if (DefInit *Group = dyn_cast<DefInit>(R.getValueInit("Group"))) {
Ted Kremenek72492392012-08-07 05:01:49 +00001229 const Record *GroupRec = Group->getDef();
1230 const std::string &GroupName = GroupRec->getValueAsString("GroupName");
Joerg Sonnenberger691a16b2012-10-25 16:37:08 +00001231 PrintFatalError(R.getLoc(), "Error " + R.getName() +
1232 " cannot be in a warning group [" + GroupName + "]");
Ted Kremenek72492392012-08-07 05:01:49 +00001233 }
1234 }
1235
Tobias Grosser74160242014-02-28 09:11:08 +00001236 // Check that all remarks have an associated diagnostic group.
1237 if (isRemark(R)) {
1238 if (!isa<DefInit>(R.getValueInit("Group"))) {
1239 PrintFatalError(R.getLoc(), "Error " + R.getName() +
1240 " not in any diagnostic group");
1241 }
1242 }
1243
Peter Collingbournebee583f2011-10-06 13:03:08 +00001244 // Filter by component.
1245 if (!Component.empty() && Component != R.getValueAsString("Component"))
1246 continue;
1247
1248 OS << "DIAG(" << R.getName() << ", ";
1249 OS << R.getValueAsDef("Class")->getName();
Alp Toker46df1c02014-06-12 10:15:20 +00001250 OS << ", (unsigned)diag::Severity::"
1251 << R.getValueAsDef("DefaultSeverity")->getValueAsString("Name");
Craig Topperf3932e32013-07-19 21:43:59 +00001252
Peter Collingbournebee583f2011-10-06 13:03:08 +00001253 // Description string.
1254 OS << ", \"";
Eric Fiselierb87be182018-05-19 03:12:04 +00001255 OS.write_escaped(DiagTextBuilder.buildForDefinition(&R)) << '"';
Craig Topperf3932e32013-07-19 21:43:59 +00001256
Benjamin Kramera8cafe22012-02-15 20:57:03 +00001257 // Warning associated with the diagnostic. This is stored as an index into
1258 // the alphabetically sorted warning table.
Sean Silva1c4aaa82012-10-10 20:25:43 +00001259 if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) {
Argyrios Kyrtzidis87acf192012-03-06 00:00:38 +00001260 std::map<std::string, GroupInfo>::iterator I =
1261 DiagsInGroup.find(DI->getDef()->getValueAsString("GroupName"));
1262 assert(I != DiagsInGroup.end());
1263 OS << ", " << I->second.IDNo;
Ted Kremenekb22ea2a2012-07-07 05:53:30 +00001264 } else if (DiagsInPedantic.count(&R)) {
1265 std::map<std::string, GroupInfo>::iterator I =
1266 DiagsInGroup.find("pedantic");
1267 assert(I != DiagsInGroup.end() && "pedantic group not defined");
1268 OS << ", " << I->second.IDNo;
Peter Collingbournebee583f2011-10-06 13:03:08 +00001269 } else {
Benjamin Kramera8cafe22012-02-15 20:57:03 +00001270 OS << ", 0";
Peter Collingbournebee583f2011-10-06 13:03:08 +00001271 }
1272
Richard Smith16e1b072013-11-12 02:41:45 +00001273 // SFINAE response.
1274 OS << ", " << R.getValueAsDef("SFINAE")->getName();
1275
1276 // Default warning has no Werror bit.
1277 if (R.getValueAsBit("WarningNoWerror"))
Peter Collingbournebee583f2011-10-06 13:03:08 +00001278 OS << ", true";
1279 else
1280 OS << ", false";
1281
Alp Toker04278ec2014-06-16 13:56:47 +00001282 if (R.getValueAsBit("ShowInSystemHeader"))
Peter Collingbournebee583f2011-10-06 13:03:08 +00001283 OS << ", true";
1284 else
1285 OS << ", false";
1286
Peter Collingbournebee583f2011-10-06 13:03:08 +00001287 // Category number.
1288 OS << ", " << CategoryIDs.getID(getDiagnosticCategory(&R, DGParentMap));
Peter Collingbournebee583f2011-10-06 13:03:08 +00001289 OS << ")\n";
1290 }
1291}
Jakob Stoklund Olesen995e0e12012-06-13 05:12:41 +00001292} // end namespace clang
Peter Collingbournebee583f2011-10-06 13:03:08 +00001293
1294//===----------------------------------------------------------------------===//
1295// Warning Group Tables generation
1296//===----------------------------------------------------------------------===//
1297
1298static std::string getDiagCategoryEnum(llvm::StringRef name) {
1299 if (name.empty())
1300 return "DiagCat_None";
Dylan Noblesmithf1a13f22012-02-13 12:32:26 +00001301 SmallString<256> enumName = llvm::StringRef("DiagCat_");
Peter Collingbournebee583f2011-10-06 13:03:08 +00001302 for (llvm::StringRef::iterator I = name.begin(), E = name.end(); I != E; ++I)
1303 enumName += isalnum(*I) ? *I : '_';
1304 return enumName.str();
1305}
Craig Topperf3932e32013-07-19 21:43:59 +00001306
Adrian Prantl9fc8faf2018-05-09 01:00:01 +00001307/// Emit the array of diagnostic subgroups.
Tobias Grossercfc57bb2014-05-06 22:06:56 +00001308///
1309/// The array of diagnostic subgroups contains for each group a list of its
1310/// subgroups. The individual lists are separated by '-1'. Groups with no
1311/// subgroups are skipped.
1312///
1313/// \code
1314/// static const int16_t DiagSubGroups[] = {
1315/// /* Empty */ -1,
1316/// /* DiagSubGroup0 */ 142, -1,
1317/// /* DiagSubGroup13 */ 265, 322, 399, -1
1318/// }
1319/// \endcode
1320///
1321static void emitDiagSubGroups(std::map<std::string, GroupInfo> &DiagsInGroup,
1322 RecordVec &GroupsInPedantic, raw_ostream &OS) {
1323 OS << "static const int16_t DiagSubGroups[] = {\n"
1324 << " /* Empty */ -1,\n";
1325 for (auto const &I : DiagsInGroup) {
1326 const bool IsPedantic = I.first == "pedantic";
1327
1328 const std::vector<std::string> &SubGroups = I.second.SubGroups;
1329 if (!SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty())) {
1330 OS << " /* DiagSubGroup" << I.second.IDNo << " */ ";
1331 for (auto const &SubGroup : SubGroups) {
1332 std::map<std::string, GroupInfo>::const_iterator RI =
1333 DiagsInGroup.find(SubGroup);
1334 assert(RI != DiagsInGroup.end() && "Referenced without existing?");
1335 OS << RI->second.IDNo << ", ";
1336 }
1337 // Emit the groups implicitly in "pedantic".
1338 if (IsPedantic) {
1339 for (auto const &Group : GroupsInPedantic) {
1340 const std::string &GroupName = Group->getValueAsString("GroupName");
1341 std::map<std::string, GroupInfo>::const_iterator RI =
1342 DiagsInGroup.find(GroupName);
1343 assert(RI != DiagsInGroup.end() && "Referenced without existing?");
1344 OS << RI->second.IDNo << ", ";
1345 }
1346 }
1347
1348 OS << "-1,\n";
1349 }
1350 }
1351 OS << "};\n\n";
1352}
1353
Adrian Prantl9fc8faf2018-05-09 01:00:01 +00001354/// Emit the list of diagnostic arrays.
Tobias Grossercfc57bb2014-05-06 22:06:56 +00001355///
1356/// This data structure is a large array that contains itself arrays of varying
1357/// size. Each array represents a list of diagnostics. The different arrays are
1358/// separated by the value '-1'.
1359///
1360/// \code
1361/// static const int16_t DiagArrays[] = {
1362/// /* Empty */ -1,
1363/// /* DiagArray1 */ diag::warn_pragma_message,
1364/// -1,
1365/// /* DiagArray2 */ diag::warn_abs_too_small,
1366/// diag::warn_unsigned_abs,
1367/// diag::warn_wrong_absolute_value_type,
1368/// -1
1369/// };
1370/// \endcode
1371///
1372static void emitDiagArrays(std::map<std::string, GroupInfo> &DiagsInGroup,
1373 RecordVec &DiagsInPedantic, raw_ostream &OS) {
1374 OS << "static const int16_t DiagArrays[] = {\n"
1375 << " /* Empty */ -1,\n";
1376 for (auto const &I : DiagsInGroup) {
1377 const bool IsPedantic = I.first == "pedantic";
1378
1379 const std::vector<const Record *> &V = I.second.DiagsInGroup;
1380 if (!V.empty() || (IsPedantic && !DiagsInPedantic.empty())) {
1381 OS << " /* DiagArray" << I.second.IDNo << " */ ";
1382 for (auto *Record : V)
1383 OS << "diag::" << Record->getName() << ", ";
1384 // Emit the diagnostics implicitly in "pedantic".
1385 if (IsPedantic) {
1386 for (auto const &Diag : DiagsInPedantic)
1387 OS << "diag::" << Diag->getName() << ", ";
1388 }
1389 OS << "-1,\n";
1390 }
1391 }
1392 OS << "};\n\n";
1393}
1394
Adrian Prantl9fc8faf2018-05-09 01:00:01 +00001395/// Emit a list of group names.
Tobias Grossercfc57bb2014-05-06 22:06:56 +00001396///
1397/// This creates a long string which by itself contains a list of pascal style
1398/// strings, which consist of a length byte directly followed by the string.
1399///
1400/// \code
1401/// static const char DiagGroupNames[] = {
1402/// \000\020#pragma-messages\t#warnings\020CFString-literal"
1403/// };
1404/// \endcode
1405static void emitDiagGroupNames(StringToOffsetTable &GroupNames,
1406 raw_ostream &OS) {
1407 OS << "static const char DiagGroupNames[] = {\n";
1408 GroupNames.EmitString(OS);
1409 OS << "};\n\n";
1410}
1411
Adrian Prantl9fc8faf2018-05-09 01:00:01 +00001412/// Emit diagnostic arrays and related data structures.
Tobias Grossercfc57bb2014-05-06 22:06:56 +00001413///
1414/// This creates the actual diagnostic array, an array of diagnostic subgroups
1415/// and an array of subgroup names.
1416///
1417/// \code
1418/// #ifdef GET_DIAG_ARRAYS
1419/// static const int16_t DiagArrays[];
1420/// static const int16_t DiagSubGroups[];
1421/// static const char DiagGroupNames[];
1422/// #endif
1423/// \endcode
1424static void emitAllDiagArrays(std::map<std::string, GroupInfo> &DiagsInGroup,
1425 RecordVec &DiagsInPedantic,
1426 RecordVec &GroupsInPedantic,
1427 StringToOffsetTable &GroupNames,
1428 raw_ostream &OS) {
1429 OS << "\n#ifdef GET_DIAG_ARRAYS\n";
1430 emitDiagArrays(DiagsInGroup, DiagsInPedantic, OS);
1431 emitDiagSubGroups(DiagsInGroup, GroupsInPedantic, OS);
1432 emitDiagGroupNames(GroupNames, OS);
1433 OS << "#endif // GET_DIAG_ARRAYS\n\n";
1434}
1435
Adrian Prantl9fc8faf2018-05-09 01:00:01 +00001436/// Emit diagnostic table.
Tobias Grossercfc57bb2014-05-06 22:06:56 +00001437///
1438/// The table is sorted by the name of the diagnostic group. Each element
1439/// consists of the name of the diagnostic group (given as offset in the
1440/// group name table), a reference to a list of diagnostics (optional) and a
1441/// reference to a set of subgroups (optional).
1442///
1443/// \code
1444/// #ifdef GET_DIAG_TABLE
1445/// {/* abi */ 159, /* DiagArray11 */ 19, /* Empty */ 0},
1446/// {/* aggregate-return */ 180, /* Empty */ 0, /* Empty */ 0},
1447/// {/* all */ 197, /* Empty */ 0, /* DiagSubGroup13 */ 3},
1448/// {/* deprecated */ 1981,/* DiagArray1 */ 348, /* DiagSubGroup3 */ 9},
1449/// #endif
1450/// \endcode
1451static void emitDiagTable(std::map<std::string, GroupInfo> &DiagsInGroup,
1452 RecordVec &DiagsInPedantic,
1453 RecordVec &GroupsInPedantic,
1454 StringToOffsetTable &GroupNames, raw_ostream &OS) {
1455 unsigned MaxLen = 0;
1456
1457 for (auto const &I: DiagsInGroup)
1458 MaxLen = std::max(MaxLen, (unsigned)I.first.size());
1459
1460 OS << "\n#ifdef GET_DIAG_TABLE\n";
1461 unsigned SubGroupIndex = 1, DiagArrayIndex = 1;
1462 for (auto const &I: DiagsInGroup) {
1463 // Group option string.
1464 OS << " { /* ";
1465 if (I.first.find_first_not_of("abcdefghijklmnopqrstuvwxyz"
1466 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1467 "0123456789!@#$%^*-+=:?") !=
1468 std::string::npos)
1469 PrintFatalError("Invalid character in diagnostic group '" + I.first +
1470 "'");
1471 OS << I.first << " */ " << std::string(MaxLen - I.first.size(), ' ');
1472 // Store a pascal-style length byte at the beginning of the string.
1473 std::string Name = char(I.first.size()) + I.first;
1474 OS << GroupNames.GetOrAddStringOffset(Name, false) << ", ";
1475
1476 // Special handling for 'pedantic'.
1477 const bool IsPedantic = I.first == "pedantic";
1478
1479 // Diagnostics in the group.
1480 const std::vector<const Record *> &V = I.second.DiagsInGroup;
1481 const bool hasDiags =
1482 !V.empty() || (IsPedantic && !DiagsInPedantic.empty());
1483 if (hasDiags) {
1484 OS << "/* DiagArray" << I.second.IDNo << " */ " << DiagArrayIndex
1485 << ", ";
1486 if (IsPedantic)
1487 DiagArrayIndex += DiagsInPedantic.size();
1488 DiagArrayIndex += V.size() + 1;
1489 } else {
1490 OS << "/* Empty */ 0, ";
1491 }
1492
1493 // Subgroups.
1494 const std::vector<std::string> &SubGroups = I.second.SubGroups;
1495 const bool hasSubGroups =
1496 !SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty());
1497 if (hasSubGroups) {
1498 OS << "/* DiagSubGroup" << I.second.IDNo << " */ " << SubGroupIndex;
1499 if (IsPedantic)
1500 SubGroupIndex += GroupsInPedantic.size();
1501 SubGroupIndex += SubGroups.size() + 1;
1502 } else {
1503 OS << "/* Empty */ 0";
1504 }
1505
1506 OS << " },\n";
1507 }
1508 OS << "#endif // GET_DIAG_TABLE\n\n";
1509}
1510
Adrian Prantl9fc8faf2018-05-09 01:00:01 +00001511/// Emit the table of diagnostic categories.
Tobias Grossercfc57bb2014-05-06 22:06:56 +00001512///
1513/// The table has the form of macro calls that have two parameters. The
1514/// category's name as well as an enum that represents the category. The
1515/// table can be used by defining the macro 'CATEGORY' and including this
1516/// table right after.
1517///
1518/// \code
1519/// #ifdef GET_CATEGORY_TABLE
1520/// CATEGORY("Semantic Issue", DiagCat_Semantic_Issue)
1521/// CATEGORY("Lambda Issue", DiagCat_Lambda_Issue)
1522/// #endif
1523/// \endcode
1524static void emitCategoryTable(RecordKeeper &Records, raw_ostream &OS) {
1525 DiagCategoryIDMap CategoriesByID(Records);
1526 OS << "\n#ifdef GET_CATEGORY_TABLE\n";
1527 for (auto const &C : CategoriesByID)
1528 OS << "CATEGORY(\"" << C << "\", " << getDiagCategoryEnum(C) << ")\n";
1529 OS << "#endif // GET_CATEGORY_TABLE\n\n";
1530}
1531
Jakob Stoklund Olesen995e0e12012-06-13 05:12:41 +00001532namespace clang {
1533void EmitClangDiagGroups(RecordKeeper &Records, raw_ostream &OS) {
Peter Collingbournebee583f2011-10-06 13:03:08 +00001534 // Compute a mapping from a DiagGroup to all of its parents.
1535 DiagGroupParentMap DGParentMap(Records);
Ted Kremenekb22ea2a2012-07-07 05:53:30 +00001536
Tobias Grossercfc57bb2014-05-06 22:06:56 +00001537 std::vector<Record *> Diags = Records.getAllDerivedDefinitions("Diagnostic");
Craig Topperf3932e32013-07-19 21:43:59 +00001538
Tobias Grossercfc57bb2014-05-06 22:06:56 +00001539 std::vector<Record *> DiagGroups =
1540 Records.getAllDerivedDefinitions("DiagGroup");
Argyrios Kyrtzidis87acf192012-03-06 00:00:38 +00001541
1542 std::map<std::string, GroupInfo> DiagsInGroup;
1543 groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
Ted Kremenekb22ea2a2012-07-07 05:53:30 +00001544
1545 // All extensions are implicitly in the "pedantic" group. Record the
1546 // implicit set of groups in the "pedantic" group, and use this information
1547 // later when emitting the group information for Pedantic.
1548 RecordVec DiagsInPedantic;
1549 RecordVec GroupsInPedantic;
1550 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
1551 inferPedantic.compute(&DiagsInPedantic, &GroupsInPedantic);
1552
Craig Topperda7cf8a2013-08-29 05:18:04 +00001553 StringToOffsetTable GroupNames;
1554 for (std::map<std::string, GroupInfo>::const_iterator
Tobias Grossercfc57bb2014-05-06 22:06:56 +00001555 I = DiagsInGroup.begin(),
1556 E = DiagsInGroup.end();
1557 I != E; ++I) {
Craig Topperda7cf8a2013-08-29 05:18:04 +00001558 // Store a pascal-style length byte at the beginning of the string.
1559 std::string Name = char(I->first.size()) + I->first;
1560 GroupNames.GetOrAddStringOffset(Name, false);
1561 }
1562
Tobias Grossercfc57bb2014-05-06 22:06:56 +00001563 emitAllDiagArrays(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames,
1564 OS);
1565 emitDiagTable(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames,
1566 OS);
1567 emitCategoryTable(Records, OS);
Peter Collingbournebee583f2011-10-06 13:03:08 +00001568}
Jakob Stoklund Olesen995e0e12012-06-13 05:12:41 +00001569} // end namespace clang
Peter Collingbournebee583f2011-10-06 13:03:08 +00001570
1571//===----------------------------------------------------------------------===//
1572// Diagnostic name index generation
1573//===----------------------------------------------------------------------===//
1574
1575namespace {
1576struct RecordIndexElement
1577{
1578 RecordIndexElement() {}
1579 explicit RecordIndexElement(Record const &R):
1580 Name(R.getName()) {}
Craig Topperf3932e32013-07-19 21:43:59 +00001581
Peter Collingbournebee583f2011-10-06 13:03:08 +00001582 std::string Name;
1583};
Peter Collingbournebee583f2011-10-06 13:03:08 +00001584} // end anonymous namespace.
1585
Jakob Stoklund Olesen995e0e12012-06-13 05:12:41 +00001586namespace clang {
1587void EmitClangDiagsIndexName(RecordKeeper &Records, raw_ostream &OS) {
Peter Collingbournebee583f2011-10-06 13:03:08 +00001588 const std::vector<Record*> &Diags =
1589 Records.getAllDerivedDefinitions("Diagnostic");
Craig Topperf3932e32013-07-19 21:43:59 +00001590
Peter Collingbournebee583f2011-10-06 13:03:08 +00001591 std::vector<RecordIndexElement> Index;
1592 Index.reserve(Diags.size());
1593 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
Craig Topperf3932e32013-07-19 21:43:59 +00001594 const Record &R = *(Diags[i]);
Peter Collingbournebee583f2011-10-06 13:03:08 +00001595 Index.push_back(RecordIndexElement(R));
1596 }
Craig Topperf3932e32013-07-19 21:43:59 +00001597
Mandeep Singh Grangc205d8c2018-03-27 16:50:00 +00001598 llvm::sort(Index.begin(), Index.end(),
1599 [](const RecordIndexElement &Lhs, const RecordIndexElement &Rhs) {
1600 return Lhs.Name < Rhs.Name;
1601 });
Craig Topperf3932e32013-07-19 21:43:59 +00001602
Peter Collingbournebee583f2011-10-06 13:03:08 +00001603 for (unsigned i = 0, e = Index.size(); i != e; ++i) {
1604 const RecordIndexElement &R = Index[i];
Craig Topperf3932e32013-07-19 21:43:59 +00001605
Peter Collingbournebee583f2011-10-06 13:03:08 +00001606 OS << "DIAG_NAME_INDEX(" << R.Name << ")\n";
1607 }
1608}
Richard Smithb6a3b4b2016-09-12 05:58:29 +00001609
1610//===----------------------------------------------------------------------===//
1611// Diagnostic documentation generation
1612//===----------------------------------------------------------------------===//
1613
1614namespace docs {
1615namespace {
1616
Richard Smithb6a3b4b2016-09-12 05:58:29 +00001617bool isRemarkGroup(const Record *DiagGroup,
1618 const std::map<std::string, GroupInfo> &DiagsInGroup) {
1619 bool AnyRemarks = false, AnyNonRemarks = false;
1620
1621 std::function<void(StringRef)> Visit = [&](StringRef GroupName) {
1622 auto &GroupInfo = DiagsInGroup.find(GroupName)->second;
1623 for (const Record *Diag : GroupInfo.DiagsInGroup)
1624 (isRemark(*Diag) ? AnyRemarks : AnyNonRemarks) = true;
1625 for (const auto &Name : GroupInfo.SubGroups)
1626 Visit(Name);
1627 };
1628 Visit(DiagGroup->getValueAsString("GroupName"));
1629
1630 if (AnyRemarks && AnyNonRemarks)
1631 PrintFatalError(
1632 DiagGroup->getLoc(),
1633 "Diagnostic group contains both remark and non-remark diagnostics");
1634 return AnyRemarks;
1635}
1636
1637std::string getDefaultSeverity(const Record *Diag) {
1638 return Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name");
1639}
1640
1641std::set<std::string>
1642getDefaultSeverities(const Record *DiagGroup,
1643 const std::map<std::string, GroupInfo> &DiagsInGroup) {
1644 std::set<std::string> States;
1645
1646 std::function<void(StringRef)> Visit = [&](StringRef GroupName) {
1647 auto &GroupInfo = DiagsInGroup.find(GroupName)->second;
1648 for (const Record *Diag : GroupInfo.DiagsInGroup)
1649 States.insert(getDefaultSeverity(Diag));
1650 for (const auto &Name : GroupInfo.SubGroups)
1651 Visit(Name);
1652 };
1653 Visit(DiagGroup->getValueAsString("GroupName"));
1654 return States;
1655}
1656
1657void writeHeader(StringRef Str, raw_ostream &OS, char Kind = '-') {
1658 OS << Str << "\n" << std::string(Str.size(), Kind) << "\n";
1659}
1660
Eric Fiselierb87be182018-05-19 03:12:04 +00001661void writeDiagnosticText(DiagnosticTextBuilder &Builder, const Record *R,
1662 StringRef Role, raw_ostream &OS) {
1663 StringRef Text = R->getValueAsString("Text");
Richard Smithb6a3b4b2016-09-12 05:58:29 +00001664 if (Text == "%0")
1665 OS << "The text of this diagnostic is not controlled by Clang.\n\n";
1666 else {
Eric Fiselierb87be182018-05-19 03:12:04 +00001667 std::vector<std::string> Out = Builder.buildForDocumentation(Role, R);
Richard Smithb6a3b4b2016-09-12 05:58:29 +00001668 for (auto &Line : Out)
1669 OS << Line << "\n";
1670 OS << "\n";
1671 }
1672}
1673
1674} // namespace
1675} // namespace docs
1676
1677void EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS) {
1678 using namespace docs;
1679
1680 // Get the documentation introduction paragraph.
1681 const Record *Documentation = Records.getDef("GlobalDocumentation");
1682 if (!Documentation) {
1683 PrintFatalError("The Documentation top-level definition is missing, "
1684 "no documentation will be generated.");
1685 return;
1686 }
1687
1688 OS << Documentation->getValueAsString("Intro") << "\n";
1689
Eric Fiselierb87be182018-05-19 03:12:04 +00001690 DiagnosticTextBuilder Builder(Records);
1691
Richard Smithb6a3b4b2016-09-12 05:58:29 +00001692 std::vector<Record*> Diags =
1693 Records.getAllDerivedDefinitions("Diagnostic");
Eric Fiselierb87be182018-05-19 03:12:04 +00001694
Richard Smithb6a3b4b2016-09-12 05:58:29 +00001695 std::vector<Record*> DiagGroups =
1696 Records.getAllDerivedDefinitions("DiagGroup");
Mandeep Singh Grangc205d8c2018-03-27 16:50:00 +00001697 llvm::sort(DiagGroups.begin(), DiagGroups.end(), diagGroupBeforeByName);
Richard Smithb6a3b4b2016-09-12 05:58:29 +00001698
1699 DiagGroupParentMap DGParentMap(Records);
1700
1701 std::map<std::string, GroupInfo> DiagsInGroup;
1702 groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
1703
1704 // Compute the set of diagnostics that are in -Wpedantic.
1705 {
Richard Smithce9d5862016-09-14 01:51:10 +00001706 RecordSet DiagsInPedanticSet;
1707 RecordSet GroupsInPedanticSet;
Richard Smithb6a3b4b2016-09-12 05:58:29 +00001708 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
Richard Smithce9d5862016-09-14 01:51:10 +00001709 inferPedantic.compute(&DiagsInPedanticSet, &GroupsInPedanticSet);
Richard Smithb6a3b4b2016-09-12 05:58:29 +00001710 auto &PedDiags = DiagsInGroup["pedantic"];
Richard Smithce9d5862016-09-14 01:51:10 +00001711 // Put the diagnostics into a deterministic order.
1712 RecordVec DiagsInPedantic(DiagsInPedanticSet.begin(),
1713 DiagsInPedanticSet.end());
1714 RecordVec GroupsInPedantic(GroupsInPedanticSet.begin(),
1715 GroupsInPedanticSet.end());
Mandeep Singh Grangc205d8c2018-03-27 16:50:00 +00001716 llvm::sort(DiagsInPedantic.begin(), DiagsInPedantic.end(),
1717 beforeThanCompare);
1718 llvm::sort(GroupsInPedantic.begin(), GroupsInPedantic.end(),
1719 beforeThanCompare);
Richard Smithb6a3b4b2016-09-12 05:58:29 +00001720 PedDiags.DiagsInGroup.insert(PedDiags.DiagsInGroup.end(),
1721 DiagsInPedantic.begin(),
1722 DiagsInPedantic.end());
1723 for (auto *Group : GroupsInPedantic)
1724 PedDiags.SubGroups.push_back(Group->getValueAsString("GroupName"));
1725 }
1726
1727 // FIXME: Write diagnostic categories and link to diagnostic groups in each.
1728
1729 // Write out the diagnostic groups.
1730 for (const Record *G : DiagGroups) {
1731 bool IsRemarkGroup = isRemarkGroup(G, DiagsInGroup);
1732 auto &GroupInfo = DiagsInGroup[G->getValueAsString("GroupName")];
1733 bool IsSynonym = GroupInfo.DiagsInGroup.empty() &&
1734 GroupInfo.SubGroups.size() == 1;
1735
Craig Topper00648582017-05-31 19:01:22 +00001736 writeHeader(((IsRemarkGroup ? "-R" : "-W") +
1737 G->getValueAsString("GroupName")).str(),
Richard Smithb6a3b4b2016-09-12 05:58:29 +00001738 OS);
1739
1740 if (!IsSynonym) {
1741 // FIXME: Ideally, all the diagnostics in a group should have the same
1742 // default state, but that is not currently the case.
1743 auto DefaultSeverities = getDefaultSeverities(G, DiagsInGroup);
1744 if (!DefaultSeverities.empty() && !DefaultSeverities.count("Ignored")) {
1745 bool AnyNonErrors = DefaultSeverities.count("Warning") ||
1746 DefaultSeverities.count("Remark");
1747 if (!AnyNonErrors)
1748 OS << "This diagnostic is an error by default, but the flag ``-Wno-"
1749 << G->getValueAsString("GroupName") << "`` can be used to disable "
1750 << "the error.\n\n";
1751 else
1752 OS << "This diagnostic is enabled by default.\n\n";
1753 } else if (DefaultSeverities.size() > 1) {
1754 OS << "Some of the diagnostics controlled by this flag are enabled "
1755 << "by default.\n\n";
1756 }
1757 }
1758
1759 if (!GroupInfo.SubGroups.empty()) {
1760 if (IsSynonym)
1761 OS << "Synonym for ";
1762 else if (GroupInfo.DiagsInGroup.empty())
1763 OS << "Controls ";
1764 else
1765 OS << "Also controls ";
1766
1767 bool First = true;
Mandeep Singh Grangc205d8c2018-03-27 16:50:00 +00001768 llvm::sort(GroupInfo.SubGroups.begin(), GroupInfo.SubGroups.end());
Richard Smithb6a3b4b2016-09-12 05:58:29 +00001769 for (const auto &Name : GroupInfo.SubGroups) {
1770 if (!First) OS << ", ";
1771 OS << "`" << (IsRemarkGroup ? "-R" : "-W") << Name << "`_";
1772 First = false;
1773 }
1774 OS << ".\n\n";
1775 }
1776
1777 if (!GroupInfo.DiagsInGroup.empty()) {
1778 OS << "**Diagnostic text:**\n\n";
1779 for (const Record *D : GroupInfo.DiagsInGroup) {
1780 auto Severity = getDefaultSeverity(D);
1781 Severity[0] = tolower(Severity[0]);
1782 if (Severity == "ignored")
1783 Severity = IsRemarkGroup ? "remark" : "warning";
Eric Fiselierb87be182018-05-19 03:12:04 +00001784
1785 writeDiagnosticText(Builder, D, Severity, OS);
Richard Smithb6a3b4b2016-09-12 05:58:29 +00001786 }
1787 }
1788
1789 auto Doc = G->getValueAsString("Documentation");
1790 if (!Doc.empty())
1791 OS << Doc;
1792 else if (GroupInfo.SubGroups.empty() && GroupInfo.DiagsInGroup.empty())
1793 OS << "This diagnostic flag exists for GCC compatibility, and has no "
1794 "effect in Clang.\n";
1795 OS << "\n";
1796 }
1797}
1798
Jakob Stoklund Olesen995e0e12012-06-13 05:12:41 +00001799} // end namespace clang