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