1 //=- ClangDiagnosticsEmitter.cpp - Generate Clang diagnostics tables -*- C++ -*-
2 //
3 // 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
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // These tablegen backends emit Clang diagnostics tables.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "TableGenBackends.h"
14 #include "llvm/ADT/DenseSet.h"
15 #include "llvm/ADT/Optional.h"
16 #include "llvm/ADT/PointerUnion.h"
17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/ADT/SmallPtrSet.h"
19 #include "llvm/ADT/SmallString.h"
20 #include "llvm/ADT/SmallVector.h"
21 #include "llvm/ADT/StringMap.h"
22 #include "llvm/ADT/StringSwitch.h"
23 #include "llvm/ADT/Twine.h"
24 #include "llvm/Support/Casting.h"
25 #include "llvm/TableGen/Error.h"
26 #include "llvm/TableGen/Record.h"
27 #include "llvm/TableGen/StringToOffsetTable.h"
28 #include "llvm/TableGen/TableGenBackend.h"
29 #include <algorithm>
30 #include <cctype>
31 #include <functional>
32 #include <map>
33 #include <set>
34 using namespace llvm;
35
36 //===----------------------------------------------------------------------===//
37 // Diagnostic category computation code.
38 //===----------------------------------------------------------------------===//
39
40 namespace {
41 class DiagGroupParentMap {
42 RecordKeeper &Records;
43 std::map<const Record*, std::vector<Record*> > Mapping;
44 public:
DiagGroupParentMap(RecordKeeper & records)45 DiagGroupParentMap(RecordKeeper &records) : Records(records) {
46 std::vector<Record*> DiagGroups
47 = Records.getAllDerivedDefinitions("DiagGroup");
48 for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) {
49 std::vector<Record*> SubGroups =
50 DiagGroups[i]->getValueAsListOfDefs("SubGroups");
51 for (unsigned j = 0, e = SubGroups.size(); j != e; ++j)
52 Mapping[SubGroups[j]].push_back(DiagGroups[i]);
53 }
54 }
55
getParents(const Record * Group)56 const std::vector<Record*> &getParents(const Record *Group) {
57 return Mapping[Group];
58 }
59 };
60 } // end anonymous namespace.
61
62 static std::string
getCategoryFromDiagGroup(const Record * Group,DiagGroupParentMap & DiagGroupParents)63 getCategoryFromDiagGroup(const Record *Group,
64 DiagGroupParentMap &DiagGroupParents) {
65 // If the DiagGroup has a category, return it.
66 std::string CatName = std::string(Group->getValueAsString("CategoryName"));
67 if (!CatName.empty()) return CatName;
68
69 // The diag group may the subgroup of one or more other diagnostic groups,
70 // check these for a category as well.
71 const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
72 for (unsigned i = 0, e = Parents.size(); i != e; ++i) {
73 CatName = getCategoryFromDiagGroup(Parents[i], DiagGroupParents);
74 if (!CatName.empty()) return CatName;
75 }
76 return "";
77 }
78
79 /// getDiagnosticCategory - Return the category that the specified diagnostic
80 /// lives in.
getDiagnosticCategory(const Record * R,DiagGroupParentMap & DiagGroupParents)81 static std::string getDiagnosticCategory(const Record *R,
82 DiagGroupParentMap &DiagGroupParents) {
83 // If the diagnostic is in a group, and that group has a category, use it.
84 if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) {
85 // Check the diagnostic's diag group for a category.
86 std::string CatName = getCategoryFromDiagGroup(Group->getDef(),
87 DiagGroupParents);
88 if (!CatName.empty()) return CatName;
89 }
90
91 // If the diagnostic itself has a category, get it.
92 return std::string(R->getValueAsString("CategoryName"));
93 }
94
95 namespace {
96 class DiagCategoryIDMap {
97 RecordKeeper &Records;
98 StringMap<unsigned> CategoryIDs;
99 std::vector<std::string> CategoryStrings;
100 public:
DiagCategoryIDMap(RecordKeeper & records)101 DiagCategoryIDMap(RecordKeeper &records) : Records(records) {
102 DiagGroupParentMap ParentInfo(Records);
103
104 // The zero'th category is "".
105 CategoryStrings.push_back("");
106 CategoryIDs[""] = 0;
107
108 std::vector<Record*> Diags =
109 Records.getAllDerivedDefinitions("Diagnostic");
110 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
111 std::string Category = getDiagnosticCategory(Diags[i], ParentInfo);
112 if (Category.empty()) continue; // Skip diags with no category.
113
114 unsigned &ID = CategoryIDs[Category];
115 if (ID != 0) continue; // Already seen.
116
117 ID = CategoryStrings.size();
118 CategoryStrings.push_back(Category);
119 }
120 }
121
getID(StringRef CategoryString)122 unsigned getID(StringRef CategoryString) {
123 return CategoryIDs[CategoryString];
124 }
125
126 typedef std::vector<std::string>::const_iterator const_iterator;
begin() const127 const_iterator begin() const { return CategoryStrings.begin(); }
end() const128 const_iterator end() const { return CategoryStrings.end(); }
129 };
130
131 struct GroupInfo {
132 std::vector<const Record*> DiagsInGroup;
133 std::vector<std::string> SubGroups;
134 unsigned IDNo;
135
136 const Record *ExplicitDef;
137
GroupInfo__anon268c2c930211::GroupInfo138 GroupInfo() : IDNo(0), ExplicitDef(nullptr) {}
139 };
140 } // end anonymous namespace.
141
beforeThanCompare(const Record * LHS,const Record * RHS)142 static bool beforeThanCompare(const Record *LHS, const Record *RHS) {
143 assert(!LHS->getLoc().empty() && !RHS->getLoc().empty());
144 return
145 LHS->getLoc().front().getPointer() < RHS->getLoc().front().getPointer();
146 }
147
diagGroupBeforeByName(const Record * LHS,const Record * RHS)148 static bool diagGroupBeforeByName(const Record *LHS, const Record *RHS) {
149 return LHS->getValueAsString("GroupName") <
150 RHS->getValueAsString("GroupName");
151 }
152
beforeThanCompareGroups(const GroupInfo * LHS,const GroupInfo * RHS)153 static bool beforeThanCompareGroups(const GroupInfo *LHS, const GroupInfo *RHS){
154 assert(!LHS->DiagsInGroup.empty() && !RHS->DiagsInGroup.empty());
155 return beforeThanCompare(LHS->DiagsInGroup.front(),
156 RHS->DiagsInGroup.front());
157 }
158
159 /// Invert the 1-[0/1] mapping of diags to group into a one to many
160 /// mapping of groups to diags in the group.
groupDiagnostics(const std::vector<Record * > & Diags,const std::vector<Record * > & DiagGroups,std::map<std::string,GroupInfo> & DiagsInGroup)161 static void groupDiagnostics(const std::vector<Record*> &Diags,
162 const std::vector<Record*> &DiagGroups,
163 std::map<std::string, GroupInfo> &DiagsInGroup) {
164
165 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
166 const Record *R = Diags[i];
167 DefInit *DI = dyn_cast<DefInit>(R->getValueInit("Group"));
168 if (!DI)
169 continue;
170 assert(R->getValueAsDef("Class")->getName() != "CLASS_NOTE" &&
171 "Note can't be in a DiagGroup");
172 std::string GroupName =
173 std::string(DI->getDef()->getValueAsString("GroupName"));
174 DiagsInGroup[GroupName].DiagsInGroup.push_back(R);
175 }
176
177 typedef SmallPtrSet<GroupInfo *, 16> GroupSetTy;
178 GroupSetTy ImplicitGroups;
179
180 // Add all DiagGroup's to the DiagsInGroup list to make sure we pick up empty
181 // groups (these are warnings that GCC supports that clang never produces).
182 for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) {
183 Record *Group = DiagGroups[i];
184 GroupInfo &GI =
185 DiagsInGroup[std::string(Group->getValueAsString("GroupName"))];
186 if (Group->isAnonymous()) {
187 if (GI.DiagsInGroup.size() > 1)
188 ImplicitGroups.insert(&GI);
189 } else {
190 if (GI.ExplicitDef)
191 assert(GI.ExplicitDef == Group);
192 else
193 GI.ExplicitDef = Group;
194 }
195
196 std::vector<Record*> SubGroups = Group->getValueAsListOfDefs("SubGroups");
197 for (unsigned j = 0, e = SubGroups.size(); j != e; ++j)
198 GI.SubGroups.push_back(
199 std::string(SubGroups[j]->getValueAsString("GroupName")));
200 }
201
202 // Assign unique ID numbers to the groups.
203 unsigned IDNo = 0;
204 for (std::map<std::string, GroupInfo>::iterator
205 I = DiagsInGroup.begin(), E = DiagsInGroup.end(); I != E; ++I, ++IDNo)
206 I->second.IDNo = IDNo;
207
208 // Sort the implicit groups, so we can warn about them deterministically.
209 SmallVector<GroupInfo *, 16> SortedGroups(ImplicitGroups.begin(),
210 ImplicitGroups.end());
211 for (SmallVectorImpl<GroupInfo *>::iterator I = SortedGroups.begin(),
212 E = SortedGroups.end();
213 I != E; ++I) {
214 MutableArrayRef<const Record *> GroupDiags = (*I)->DiagsInGroup;
215 llvm::sort(GroupDiags, beforeThanCompare);
216 }
217 llvm::sort(SortedGroups, beforeThanCompareGroups);
218
219 // Warn about the same group being used anonymously in multiple places.
220 for (SmallVectorImpl<GroupInfo *>::const_iterator I = SortedGroups.begin(),
221 E = SortedGroups.end();
222 I != E; ++I) {
223 ArrayRef<const Record *> GroupDiags = (*I)->DiagsInGroup;
224
225 if ((*I)->ExplicitDef) {
226 std::string Name =
227 std::string((*I)->ExplicitDef->getValueAsString("GroupName"));
228 for (ArrayRef<const Record *>::const_iterator DI = GroupDiags.begin(),
229 DE = GroupDiags.end();
230 DI != DE; ++DI) {
231 const DefInit *GroupInit = cast<DefInit>((*DI)->getValueInit("Group"));
232 const Record *NextDiagGroup = GroupInit->getDef();
233 if (NextDiagGroup == (*I)->ExplicitDef)
234 continue;
235
236 SrcMgr.PrintMessage((*DI)->getLoc().front(),
237 SourceMgr::DK_Error,
238 Twine("group '") + Name +
239 "' is referred to anonymously");
240 SrcMgr.PrintMessage((*I)->ExplicitDef->getLoc().front(),
241 SourceMgr::DK_Note, "group defined here");
242 }
243 } else {
244 // If there's no existing named group, we should just warn once and use
245 // notes to list all the other cases.
246 ArrayRef<const Record *>::const_iterator DI = GroupDiags.begin(),
247 DE = GroupDiags.end();
248 assert(DI != DE && "We only care about groups with multiple uses!");
249
250 const DefInit *GroupInit = cast<DefInit>((*DI)->getValueInit("Group"));
251 const Record *NextDiagGroup = GroupInit->getDef();
252 std::string Name =
253 std::string(NextDiagGroup->getValueAsString("GroupName"));
254
255 SrcMgr.PrintMessage((*DI)->getLoc().front(),
256 SourceMgr::DK_Error,
257 Twine("group '") + Name +
258 "' is referred to anonymously");
259
260 for (++DI; DI != DE; ++DI) {
261 SrcMgr.PrintMessage((*DI)->getLoc().front(),
262 SourceMgr::DK_Note, "also referenced here");
263 }
264 }
265 }
266 }
267
268 //===----------------------------------------------------------------------===//
269 // Infer members of -Wpedantic.
270 //===----------------------------------------------------------------------===//
271
272 typedef std::vector<const Record *> RecordVec;
273 typedef llvm::DenseSet<const Record *> RecordSet;
274 typedef llvm::PointerUnion<RecordVec*, RecordSet*> VecOrSet;
275
276 namespace {
277 class InferPedantic {
278 typedef llvm::DenseMap<const Record*,
279 std::pair<unsigned, Optional<unsigned> > > GMap;
280
281 DiagGroupParentMap &DiagGroupParents;
282 const std::vector<Record*> &Diags;
283 const std::vector<Record*> DiagGroups;
284 std::map<std::string, GroupInfo> &DiagsInGroup;
285 llvm::DenseSet<const Record*> DiagsSet;
286 GMap GroupCount;
287 public:
InferPedantic(DiagGroupParentMap & DiagGroupParents,const std::vector<Record * > & Diags,const std::vector<Record * > & DiagGroups,std::map<std::string,GroupInfo> & DiagsInGroup)288 InferPedantic(DiagGroupParentMap &DiagGroupParents,
289 const std::vector<Record*> &Diags,
290 const std::vector<Record*> &DiagGroups,
291 std::map<std::string, GroupInfo> &DiagsInGroup)
292 : DiagGroupParents(DiagGroupParents),
293 Diags(Diags),
294 DiagGroups(DiagGroups),
295 DiagsInGroup(DiagsInGroup) {}
296
297 /// Compute the set of diagnostics and groups that are immediately
298 /// in -Wpedantic.
299 void compute(VecOrSet DiagsInPedantic,
300 VecOrSet GroupsInPedantic);
301
302 private:
303 /// Determine whether a group is a subgroup of another group.
304 bool isSubGroupOfGroup(const Record *Group,
305 llvm::StringRef RootGroupName);
306
307 /// Determine if the diagnostic is an extension.
308 bool isExtension(const Record *Diag);
309
310 /// Determine if the diagnostic is off by default.
311 bool isOffByDefault(const Record *Diag);
312
313 /// Increment the count for a group, and transitively marked
314 /// parent groups when appropriate.
315 void markGroup(const Record *Group);
316
317 /// Return true if the diagnostic is in a pedantic group.
318 bool groupInPedantic(const Record *Group, bool increment = false);
319 };
320 } // end anonymous namespace
321
isSubGroupOfGroup(const Record * Group,llvm::StringRef GName)322 bool InferPedantic::isSubGroupOfGroup(const Record *Group,
323 llvm::StringRef GName) {
324 const std::string &GroupName =
325 std::string(Group->getValueAsString("GroupName"));
326 if (GName == GroupName)
327 return true;
328
329 const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
330 for (unsigned i = 0, e = Parents.size(); i != e; ++i)
331 if (isSubGroupOfGroup(Parents[i], GName))
332 return true;
333
334 return false;
335 }
336
337 /// Determine if the diagnostic is an extension.
isExtension(const Record * Diag)338 bool InferPedantic::isExtension(const Record *Diag) {
339 const std::string &ClsName =
340 std::string(Diag->getValueAsDef("Class")->getName());
341 return ClsName == "CLASS_EXTENSION";
342 }
343
isOffByDefault(const Record * Diag)344 bool InferPedantic::isOffByDefault(const Record *Diag) {
345 const std::string &DefSeverity = std::string(
346 Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name"));
347 return DefSeverity == "Ignored";
348 }
349
groupInPedantic(const Record * Group,bool increment)350 bool InferPedantic::groupInPedantic(const Record *Group, bool increment) {
351 GMap::mapped_type &V = GroupCount[Group];
352 // Lazily compute the threshold value for the group count.
353 if (!V.second.hasValue()) {
354 const GroupInfo &GI =
355 DiagsInGroup[std::string(Group->getValueAsString("GroupName"))];
356 V.second = GI.SubGroups.size() + GI.DiagsInGroup.size();
357 }
358
359 if (increment)
360 ++V.first;
361
362 // Consider a group in -Wpendatic IFF if has at least one diagnostic
363 // or subgroup AND all of those diagnostics and subgroups are covered
364 // by -Wpedantic via our computation.
365 return V.first != 0 && V.first == V.second.getValue();
366 }
367
markGroup(const Record * Group)368 void InferPedantic::markGroup(const Record *Group) {
369 // If all the diagnostics and subgroups have been marked as being
370 // covered by -Wpedantic, increment the count of parent groups. Once the
371 // group's count is equal to the number of subgroups and diagnostics in
372 // that group, we can safely add this group to -Wpedantic.
373 if (groupInPedantic(Group, /* increment */ true)) {
374 const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
375 for (unsigned i = 0, e = Parents.size(); i != e; ++i)
376 markGroup(Parents[i]);
377 }
378 }
379
compute(VecOrSet DiagsInPedantic,VecOrSet GroupsInPedantic)380 void InferPedantic::compute(VecOrSet DiagsInPedantic,
381 VecOrSet GroupsInPedantic) {
382 // All extensions that are not on by default are implicitly in the
383 // "pedantic" group. For those that aren't explicitly included in -Wpedantic,
384 // mark them for consideration to be included in -Wpedantic directly.
385 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
386 Record *R = Diags[i];
387 if (isExtension(R) && isOffByDefault(R)) {
388 DiagsSet.insert(R);
389 if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) {
390 const Record *GroupRec = Group->getDef();
391 if (!isSubGroupOfGroup(GroupRec, "pedantic")) {
392 markGroup(GroupRec);
393 }
394 }
395 }
396 }
397
398 // Compute the set of diagnostics that are directly in -Wpedantic. We
399 // march through Diags a second time to ensure the results are emitted
400 // in deterministic order.
401 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
402 Record *R = Diags[i];
403 if (!DiagsSet.count(R))
404 continue;
405 // Check if the group is implicitly in -Wpedantic. If so,
406 // the diagnostic should not be directly included in the -Wpedantic
407 // diagnostic group.
408 if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group")))
409 if (groupInPedantic(Group->getDef()))
410 continue;
411
412 // The diagnostic is not included in a group that is (transitively) in
413 // -Wpedantic. Include it in -Wpedantic directly.
414 if (RecordVec *V = DiagsInPedantic.dyn_cast<RecordVec*>())
415 V->push_back(R);
416 else {
417 DiagsInPedantic.get<RecordSet*>()->insert(R);
418 }
419 }
420
421 if (!GroupsInPedantic)
422 return;
423
424 // Compute the set of groups that are directly in -Wpedantic. We
425 // march through the groups to ensure the results are emitted
426 /// in a deterministc order.
427 for (unsigned i = 0, ei = DiagGroups.size(); i != ei; ++i) {
428 Record *Group = DiagGroups[i];
429 if (!groupInPedantic(Group))
430 continue;
431
432 unsigned ParentsInPedantic = 0;
433 const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
434 for (unsigned j = 0, ej = Parents.size(); j != ej; ++j) {
435 if (groupInPedantic(Parents[j]))
436 ++ParentsInPedantic;
437 }
438 // If all the parents are in -Wpedantic, this means that this diagnostic
439 // group will be indirectly included by -Wpedantic already. In that
440 // case, do not add it directly to -Wpedantic. If the group has no
441 // parents, obviously it should go into -Wpedantic.
442 if (Parents.size() > 0 && ParentsInPedantic == Parents.size())
443 continue;
444
445 if (RecordVec *V = GroupsInPedantic.dyn_cast<RecordVec*>())
446 V->push_back(Group);
447 else {
448 GroupsInPedantic.get<RecordSet*>()->insert(Group);
449 }
450 }
451 }
452
453 namespace {
454 enum PieceKind {
455 MultiPieceClass,
456 TextPieceClass,
457 PlaceholderPieceClass,
458 SelectPieceClass,
459 PluralPieceClass,
460 DiffPieceClass,
461 SubstitutionPieceClass,
462 };
463
464 enum ModifierType {
465 MT_Unknown,
466 MT_Placeholder,
467 MT_Select,
468 MT_Sub,
469 MT_Plural,
470 MT_Diff,
471 MT_Ordinal,
472 MT_S,
473 MT_Q,
474 MT_ObjCClass,
475 MT_ObjCInstance,
476 };
477
getModifierName(ModifierType MT)478 static StringRef getModifierName(ModifierType MT) {
479 switch (MT) {
480 case MT_Select:
481 return "select";
482 case MT_Sub:
483 return "sub";
484 case MT_Diff:
485 return "diff";
486 case MT_Plural:
487 return "plural";
488 case MT_Ordinal:
489 return "ordinal";
490 case MT_S:
491 return "s";
492 case MT_Q:
493 return "q";
494 case MT_Placeholder:
495 return "";
496 case MT_ObjCClass:
497 return "objcclass";
498 case MT_ObjCInstance:
499 return "objcinstance";
500 case MT_Unknown:
501 llvm_unreachable("invalid modifier type");
502 }
503 // Unhandled case
504 llvm_unreachable("invalid modifier type");
505 }
506
507 struct Piece {
508 // This type and its derived classes are move-only.
Piece__anon268c2c930411::Piece509 Piece(PieceKind Kind) : ClassKind(Kind) {}
510 Piece(Piece const &O) = delete;
511 Piece &operator=(Piece const &) = delete;
~Piece__anon268c2c930411::Piece512 virtual ~Piece() {}
513
getPieceClass__anon268c2c930411::Piece514 PieceKind getPieceClass() const { return ClassKind; }
classof__anon268c2c930411::Piece515 static bool classof(const Piece *) { return true; }
516
517 private:
518 PieceKind ClassKind;
519 };
520
521 struct MultiPiece : Piece {
MultiPiece__anon268c2c930411::MultiPiece522 MultiPiece() : Piece(MultiPieceClass) {}
MultiPiece__anon268c2c930411::MultiPiece523 MultiPiece(std::vector<Piece *> Pieces)
524 : Piece(MultiPieceClass), Pieces(std::move(Pieces)) {}
525
526 std::vector<Piece *> Pieces;
527
classof__anon268c2c930411::MultiPiece528 static bool classof(const Piece *P) {
529 return P->getPieceClass() == MultiPieceClass;
530 }
531 };
532
533 struct TextPiece : Piece {
534 StringRef Role;
535 std::string Text;
TextPiece__anon268c2c930411::TextPiece536 TextPiece(StringRef Text, StringRef Role = "")
537 : Piece(TextPieceClass), Role(Role), Text(Text.str()) {}
538
classof__anon268c2c930411::TextPiece539 static bool classof(const Piece *P) {
540 return P->getPieceClass() == TextPieceClass;
541 }
542 };
543
544 struct PlaceholderPiece : Piece {
545 ModifierType Kind;
546 int Index;
PlaceholderPiece__anon268c2c930411::PlaceholderPiece547 PlaceholderPiece(ModifierType Kind, int Index)
548 : Piece(PlaceholderPieceClass), Kind(Kind), Index(Index) {}
549
classof__anon268c2c930411::PlaceholderPiece550 static bool classof(const Piece *P) {
551 return P->getPieceClass() == PlaceholderPieceClass;
552 }
553 };
554
555 struct SelectPiece : Piece {
556 protected:
SelectPiece__anon268c2c930411::SelectPiece557 SelectPiece(PieceKind Kind, ModifierType ModKind)
558 : Piece(Kind), ModKind(ModKind) {}
559
560 public:
SelectPiece__anon268c2c930411::SelectPiece561 SelectPiece(ModifierType ModKind) : SelectPiece(SelectPieceClass, ModKind) {}
562
563 ModifierType ModKind;
564 std::vector<Piece *> Options;
565 int Index = 0;
566
classof__anon268c2c930411::SelectPiece567 static bool classof(const Piece *P) {
568 return P->getPieceClass() == SelectPieceClass ||
569 P->getPieceClass() == PluralPieceClass;
570 }
571 };
572
573 struct PluralPiece : SelectPiece {
PluralPiece__anon268c2c930411::PluralPiece574 PluralPiece() : SelectPiece(PluralPieceClass, MT_Plural) {}
575
576 std::vector<Piece *> OptionPrefixes;
577 int Index = 0;
578
classof__anon268c2c930411::PluralPiece579 static bool classof(const Piece *P) {
580 return P->getPieceClass() == PluralPieceClass;
581 }
582 };
583
584 struct DiffPiece : Piece {
DiffPiece__anon268c2c930411::DiffPiece585 DiffPiece() : Piece(DiffPieceClass) {}
586
587 Piece *Options[2] = {};
588 int Indexes[2] = {};
589
classof__anon268c2c930411::DiffPiece590 static bool classof(const Piece *P) {
591 return P->getPieceClass() == DiffPieceClass;
592 }
593 };
594
595 struct SubstitutionPiece : Piece {
SubstitutionPiece__anon268c2c930411::SubstitutionPiece596 SubstitutionPiece() : Piece(SubstitutionPieceClass) {}
597
598 std::string Name;
599 std::vector<int> Modifiers;
600
classof__anon268c2c930411::SubstitutionPiece601 static bool classof(const Piece *P) {
602 return P->getPieceClass() == SubstitutionPieceClass;
603 }
604 };
605
606 /// Diagnostic text, parsed into pieces.
607
608
609 struct DiagnosticTextBuilder {
610 DiagnosticTextBuilder(DiagnosticTextBuilder const &) = delete;
611 DiagnosticTextBuilder &operator=(DiagnosticTextBuilder const &) = delete;
612
DiagnosticTextBuilder__anon268c2c930411::DiagnosticTextBuilder613 DiagnosticTextBuilder(RecordKeeper &Records) {
614 // Build up the list of substitution records.
615 for (auto *S : Records.getAllDerivedDefinitions("TextSubstitution")) {
616 EvaluatingRecordGuard Guard(&EvaluatingRecord, S);
617 Substitutions.try_emplace(
618 S->getName(), DiagText(*this, S->getValueAsString("Substitution")));
619 }
620
621 // Check that no diagnostic definitions have the same name as a
622 // substitution.
623 for (Record *Diag : Records.getAllDerivedDefinitions("Diagnostic")) {
624 StringRef Name = Diag->getName();
625 if (Substitutions.count(Name))
626 llvm::PrintFatalError(
627 Diag->getLoc(),
628 "Diagnostic '" + Name +
629 "' has same name as TextSubstitution definition");
630 }
631 }
632
633 std::vector<std::string> buildForDocumentation(StringRef Role,
634 const Record *R);
635 std::string buildForDefinition(const Record *R);
636
getSubstitution__anon268c2c930411::DiagnosticTextBuilder637 Piece *getSubstitution(SubstitutionPiece *S) const {
638 auto It = Substitutions.find(S->Name);
639 if (It == Substitutions.end())
640 PrintFatalError("Failed to find substitution with name: " + S->Name);
641 return It->second.Root;
642 }
643
PrintFatalError__anon268c2c930411::DiagnosticTextBuilder644 LLVM_ATTRIBUTE_NORETURN void PrintFatalError(llvm::Twine const &Msg) const {
645 assert(EvaluatingRecord && "not evaluating a record?");
646 llvm::PrintFatalError(EvaluatingRecord->getLoc(), Msg);
647 }
648
649 private:
650 struct DiagText {
651 DiagnosticTextBuilder &Builder;
652 std::vector<Piece *> AllocatedPieces;
653 Piece *Root = nullptr;
654
New__anon268c2c930411::DiagnosticTextBuilder::DiagText655 template <class T, class... Args> T *New(Args &&... args) {
656 static_assert(std::is_base_of<Piece, T>::value, "must be piece");
657 T *Mem = new T(std::forward<Args>(args)...);
658 AllocatedPieces.push_back(Mem);
659 return Mem;
660 }
661
DiagText__anon268c2c930411::DiagnosticTextBuilder::DiagText662 DiagText(DiagnosticTextBuilder &Builder, StringRef Text)
663 : Builder(Builder), Root(parseDiagText(Text)) {}
664
665 Piece *parseDiagText(StringRef &Text, bool Nested = false);
666 int parseModifier(StringRef &) const;
667
668 public:
DiagText__anon268c2c930411::DiagnosticTextBuilder::DiagText669 DiagText(DiagText &&O) noexcept
670 : Builder(O.Builder), AllocatedPieces(std::move(O.AllocatedPieces)),
671 Root(O.Root) {
672 O.Root = nullptr;
673 }
674
~DiagText__anon268c2c930411::DiagnosticTextBuilder::DiagText675 ~DiagText() {
676 for (Piece *P : AllocatedPieces)
677 delete P;
678 }
679 };
680
681 private:
682 const Record *EvaluatingRecord = nullptr;
683 struct EvaluatingRecordGuard {
EvaluatingRecordGuard__anon268c2c930411::DiagnosticTextBuilder::EvaluatingRecordGuard684 EvaluatingRecordGuard(const Record **Dest, const Record *New)
685 : Dest(Dest), Old(*Dest) {
686 *Dest = New;
687 }
~EvaluatingRecordGuard__anon268c2c930411::DiagnosticTextBuilder::EvaluatingRecordGuard688 ~EvaluatingRecordGuard() { *Dest = Old; }
689 const Record **Dest;
690 const Record *Old;
691 };
692
693 StringMap<DiagText> Substitutions;
694 };
695
696 template <class Derived> struct DiagTextVisitor {
697 using ModifierMappingsType = Optional<std::vector<int>>;
698
699 private:
getDerived__anon268c2c930411::DiagTextVisitor700 Derived &getDerived() { return static_cast<Derived &>(*this); }
701
702 public:
703 std::vector<int>
getSubstitutionMappings__anon268c2c930411::DiagTextVisitor704 getSubstitutionMappings(SubstitutionPiece *P,
705 const ModifierMappingsType &Mappings) const {
706 std::vector<int> NewMappings;
707 for (int Idx : P->Modifiers)
708 NewMappings.push_back(mapIndex(Idx, Mappings));
709 return NewMappings;
710 }
711
712 struct SubstitutionContext {
SubstitutionContext__anon268c2c930411::DiagTextVisitor::SubstitutionContext713 SubstitutionContext(DiagTextVisitor &Visitor, SubstitutionPiece *P)
714 : Visitor(Visitor) {
715 Substitution = Visitor.Builder.getSubstitution(P);
716 OldMappings = std::move(Visitor.ModifierMappings);
717 std::vector<int> NewMappings =
718 Visitor.getSubstitutionMappings(P, OldMappings);
719 Visitor.ModifierMappings = std::move(NewMappings);
720 }
721
~SubstitutionContext__anon268c2c930411::DiagTextVisitor::SubstitutionContext722 ~SubstitutionContext() {
723 Visitor.ModifierMappings = std::move(OldMappings);
724 }
725
726 private:
727 DiagTextVisitor &Visitor;
728 Optional<std::vector<int>> OldMappings;
729
730 public:
731 Piece *Substitution;
732 };
733
734 public:
DiagTextVisitor__anon268c2c930411::DiagTextVisitor735 DiagTextVisitor(DiagnosticTextBuilder &Builder) : Builder(Builder) {}
736
Visit__anon268c2c930411::DiagTextVisitor737 void Visit(Piece *P) {
738 switch (P->getPieceClass()) {
739 #define CASE(T) \
740 case T##PieceClass: \
741 return getDerived().Visit##T(static_cast<T##Piece *>(P))
742 CASE(Multi);
743 CASE(Text);
744 CASE(Placeholder);
745 CASE(Select);
746 CASE(Plural);
747 CASE(Diff);
748 CASE(Substitution);
749 #undef CASE
750 }
751 }
752
VisitSubstitution__anon268c2c930411::DiagTextVisitor753 void VisitSubstitution(SubstitutionPiece *P) {
754 SubstitutionContext Guard(*this, P);
755 Visit(Guard.Substitution);
756 }
757
mapIndex__anon268c2c930411::DiagTextVisitor758 int mapIndex(int Idx,
759 ModifierMappingsType const &ModifierMappings) const {
760 if (!ModifierMappings)
761 return Idx;
762 if (ModifierMappings->size() <= static_cast<unsigned>(Idx))
763 Builder.PrintFatalError("Modifier value '" + std::to_string(Idx) +
764 "' is not valid for this mapping (has " +
765 std::to_string(ModifierMappings->size()) +
766 " mappings)");
767 return (*ModifierMappings)[Idx];
768 }
769
mapIndex__anon268c2c930411::DiagTextVisitor770 int mapIndex(int Idx) const {
771 return mapIndex(Idx, ModifierMappings);
772 }
773
774 protected:
775 DiagnosticTextBuilder &Builder;
776 ModifierMappingsType ModifierMappings;
777 };
778
escapeRST(StringRef Str,std::string & Out)779 void escapeRST(StringRef Str, std::string &Out) {
780 for (auto K : Str) {
781 if (StringRef("`*|_[]\\").count(K))
782 Out.push_back('\\');
783 Out.push_back(K);
784 }
785 }
786
padToSameLength(It Begin,It End)787 template <typename It> void padToSameLength(It Begin, It End) {
788 size_t Width = 0;
789 for (It I = Begin; I != End; ++I)
790 Width = std::max(Width, I->size());
791 for (It I = Begin; I != End; ++I)
792 (*I) += std::string(Width - I->size(), ' ');
793 }
794
makeTableRows(It Begin,It End)795 template <typename It> void makeTableRows(It Begin, It End) {
796 if (Begin == End)
797 return;
798 padToSameLength(Begin, End);
799 for (It I = Begin; I != End; ++I)
800 *I = "|" + *I + "|";
801 }
802
makeRowSeparator(std::string & Str)803 void makeRowSeparator(std::string &Str) {
804 for (char &K : Str)
805 K = (K == '|' ? '+' : '-');
806 }
807
808 struct DiagTextDocPrinter : DiagTextVisitor<DiagTextDocPrinter> {
809 using BaseTy = DiagTextVisitor<DiagTextDocPrinter>;
DiagTextDocPrinter__anon268c2c930411::DiagTextDocPrinter810 DiagTextDocPrinter(DiagnosticTextBuilder &Builder,
811 std::vector<std::string> &RST)
812 : BaseTy(Builder), RST(RST) {}
813
gatherNodes__anon268c2c930411::DiagTextDocPrinter814 void gatherNodes(
815 Piece *OrigP, const ModifierMappingsType &CurrentMappings,
816 std::vector<std::pair<Piece *, ModifierMappingsType>> &Pieces) const {
817 if (auto *Sub = dyn_cast<SubstitutionPiece>(OrigP)) {
818 ModifierMappingsType NewMappings =
819 getSubstitutionMappings(Sub, CurrentMappings);
820 return gatherNodes(Builder.getSubstitution(Sub), NewMappings, Pieces);
821 }
822 if (auto *MD = dyn_cast<MultiPiece>(OrigP)) {
823 for (Piece *Node : MD->Pieces)
824 gatherNodes(Node, CurrentMappings, Pieces);
825 return;
826 }
827 Pieces.push_back(std::make_pair(OrigP, CurrentMappings));
828 }
829
VisitMulti__anon268c2c930411::DiagTextDocPrinter830 void VisitMulti(MultiPiece *P) {
831 if (P->Pieces.empty()) {
832 RST.push_back("");
833 return;
834 }
835
836 if (P->Pieces.size() == 1)
837 return Visit(P->Pieces[0]);
838
839 // Flatten the list of nodes, replacing any substitution pieces with the
840 // recursively flattened substituted node.
841 std::vector<std::pair<Piece *, ModifierMappingsType>> Pieces;
842 gatherNodes(P, ModifierMappings, Pieces);
843
844 std::string EmptyLinePrefix;
845 size_t Start = RST.size();
846 bool HasMultipleLines = true;
847 for (const std::pair<Piece *, ModifierMappingsType> &NodePair : Pieces) {
848 std::vector<std::string> Lines;
849 DiagTextDocPrinter Visitor{Builder, Lines};
850 Visitor.ModifierMappings = NodePair.second;
851 Visitor.Visit(NodePair.first);
852
853 if (Lines.empty())
854 continue;
855
856 // We need a vertical separator if either this or the previous piece is a
857 // multi-line piece, or this is the last piece.
858 const char *Separator = (Lines.size() > 1 || HasMultipleLines) ? "|" : "";
859 HasMultipleLines = Lines.size() > 1;
860
861 if (Start + Lines.size() > RST.size())
862 RST.resize(Start + Lines.size(), EmptyLinePrefix);
863
864 padToSameLength(Lines.begin(), Lines.end());
865 for (size_t I = 0; I != Lines.size(); ++I)
866 RST[Start + I] += Separator + Lines[I];
867 std::string Empty(Lines[0].size(), ' ');
868 for (size_t I = Start + Lines.size(); I != RST.size(); ++I)
869 RST[I] += Separator + Empty;
870 EmptyLinePrefix += Separator + Empty;
871 }
872 for (size_t I = Start; I != RST.size(); ++I)
873 RST[I] += "|";
874 EmptyLinePrefix += "|";
875
876 makeRowSeparator(EmptyLinePrefix);
877 RST.insert(RST.begin() + Start, EmptyLinePrefix);
878 RST.insert(RST.end(), EmptyLinePrefix);
879 }
880
VisitText__anon268c2c930411::DiagTextDocPrinter881 void VisitText(TextPiece *P) {
882 RST.push_back("");
883 auto &S = RST.back();
884
885 StringRef T = P->Text;
886 while (!T.empty() && T.front() == ' ') {
887 RST.back() += " |nbsp| ";
888 T = T.drop_front();
889 }
890
891 std::string Suffix;
892 while (!T.empty() && T.back() == ' ') {
893 Suffix += " |nbsp| ";
894 T = T.drop_back();
895 }
896
897 if (!T.empty()) {
898 S += ':';
899 S += P->Role;
900 S += ":`";
901 escapeRST(T, S);
902 S += '`';
903 }
904
905 S += Suffix;
906 }
907
VisitPlaceholder__anon268c2c930411::DiagTextDocPrinter908 void VisitPlaceholder(PlaceholderPiece *P) {
909 RST.push_back(std::string(":placeholder:`") +
910 char('A' + mapIndex(P->Index)) + "`");
911 }
912
VisitSelect__anon268c2c930411::DiagTextDocPrinter913 void VisitSelect(SelectPiece *P) {
914 std::vector<size_t> SeparatorIndexes;
915 SeparatorIndexes.push_back(RST.size());
916 RST.emplace_back();
917 for (auto *O : P->Options) {
918 Visit(O);
919 SeparatorIndexes.push_back(RST.size());
920 RST.emplace_back();
921 }
922
923 makeTableRows(RST.begin() + SeparatorIndexes.front(),
924 RST.begin() + SeparatorIndexes.back() + 1);
925 for (size_t I : SeparatorIndexes)
926 makeRowSeparator(RST[I]);
927 }
928
VisitPlural__anon268c2c930411::DiagTextDocPrinter929 void VisitPlural(PluralPiece *P) { VisitSelect(P); }
930
VisitDiff__anon268c2c930411::DiagTextDocPrinter931 void VisitDiff(DiffPiece *P) { Visit(P->Options[1]); }
932
933 std::vector<std::string> &RST;
934 };
935
936 struct DiagTextPrinter : DiagTextVisitor<DiagTextPrinter> {
937 public:
938 using BaseTy = DiagTextVisitor<DiagTextPrinter>;
DiagTextPrinter__anon268c2c930411::DiagTextPrinter939 DiagTextPrinter(DiagnosticTextBuilder &Builder, std::string &Result)
940 : BaseTy(Builder), Result(Result) {}
941
VisitMulti__anon268c2c930411::DiagTextPrinter942 void VisitMulti(MultiPiece *P) {
943 for (auto *Child : P->Pieces)
944 Visit(Child);
945 }
VisitText__anon268c2c930411::DiagTextPrinter946 void VisitText(TextPiece *P) { Result += P->Text; }
VisitPlaceholder__anon268c2c930411::DiagTextPrinter947 void VisitPlaceholder(PlaceholderPiece *P) {
948 Result += "%";
949 Result += getModifierName(P->Kind);
950 addInt(mapIndex(P->Index));
951 }
VisitSelect__anon268c2c930411::DiagTextPrinter952 void VisitSelect(SelectPiece *P) {
953 Result += "%";
954 Result += getModifierName(P->ModKind);
955 if (P->ModKind == MT_Select) {
956 Result += "{";
957 for (auto *D : P->Options) {
958 Visit(D);
959 Result += '|';
960 }
961 if (!P->Options.empty())
962 Result.erase(--Result.end());
963 Result += '}';
964 }
965 addInt(mapIndex(P->Index));
966 }
967
VisitPlural__anon268c2c930411::DiagTextPrinter968 void VisitPlural(PluralPiece *P) {
969 Result += "%plural{";
970 assert(P->Options.size() == P->OptionPrefixes.size());
971 for (unsigned I = 0, End = P->Options.size(); I < End; ++I) {
972 if (P->OptionPrefixes[I])
973 Visit(P->OptionPrefixes[I]);
974 Visit(P->Options[I]);
975 Result += "|";
976 }
977 if (!P->Options.empty())
978 Result.erase(--Result.end());
979 Result += '}';
980 addInt(mapIndex(P->Index));
981 }
982
VisitDiff__anon268c2c930411::DiagTextPrinter983 void VisitDiff(DiffPiece *P) {
984 Result += "%diff{";
985 Visit(P->Options[0]);
986 Result += "|";
987 Visit(P->Options[1]);
988 Result += "}";
989 addInt(mapIndex(P->Indexes[0]));
990 Result += ",";
991 addInt(mapIndex(P->Indexes[1]));
992 }
993
addInt__anon268c2c930411::DiagTextPrinter994 void addInt(int Val) { Result += std::to_string(Val); }
995
996 std::string &Result;
997 };
998
parseModifier(StringRef & Text) const999 int DiagnosticTextBuilder::DiagText::parseModifier(StringRef &Text) const {
1000 if (Text.empty() || !isdigit(Text[0]))
1001 Builder.PrintFatalError("expected modifier in diagnostic");
1002 int Val = 0;
1003 do {
1004 Val *= 10;
1005 Val += Text[0] - '0';
1006 Text = Text.drop_front();
1007 } while (!Text.empty() && isdigit(Text[0]));
1008 return Val;
1009 }
1010
parseDiagText(StringRef & Text,bool Nested)1011 Piece *DiagnosticTextBuilder::DiagText::parseDiagText(StringRef &Text,
1012 bool Nested) {
1013 std::vector<Piece *> Parsed;
1014
1015 while (!Text.empty()) {
1016 size_t End = (size_t)-2;
1017 do
1018 End = Nested ? Text.find_first_of("%|}", End + 2)
1019 : Text.find_first_of('%', End + 2);
1020 while (End < Text.size() - 1 && Text[End] == '%' &&
1021 (Text[End + 1] == '%' || Text[End + 1] == '|'));
1022
1023 if (End) {
1024 Parsed.push_back(New<TextPiece>(Text.slice(0, End), "diagtext"));
1025 Text = Text.slice(End, StringRef::npos);
1026 if (Text.empty())
1027 break;
1028 }
1029
1030 if (Text[0] == '|' || Text[0] == '}')
1031 break;
1032
1033 // Drop the '%'.
1034 Text = Text.drop_front();
1035
1036 // Extract the (optional) modifier.
1037 size_t ModLength = Text.find_first_of("0123456789{");
1038 StringRef Modifier = Text.slice(0, ModLength);
1039 Text = Text.slice(ModLength, StringRef::npos);
1040 ModifierType ModType = llvm::StringSwitch<ModifierType>{Modifier}
1041 .Case("select", MT_Select)
1042 .Case("sub", MT_Sub)
1043 .Case("diff", MT_Diff)
1044 .Case("plural", MT_Plural)
1045 .Case("s", MT_S)
1046 .Case("ordinal", MT_Ordinal)
1047 .Case("q", MT_Q)
1048 .Case("objcclass", MT_ObjCClass)
1049 .Case("objcinstance", MT_ObjCInstance)
1050 .Case("", MT_Placeholder)
1051 .Default(MT_Unknown);
1052
1053 switch (ModType) {
1054 case MT_Unknown:
1055 Builder.PrintFatalError("Unknown modifier type: " + Modifier);
1056 case MT_Select: {
1057 SelectPiece *Select = New<SelectPiece>(MT_Select);
1058 do {
1059 Text = Text.drop_front(); // '{' or '|'
1060 Select->Options.push_back(parseDiagText(Text, true));
1061 assert(!Text.empty() && "malformed %select");
1062 } while (Text.front() == '|');
1063 // Drop the trailing '}'.
1064 Text = Text.drop_front(1);
1065 Select->Index = parseModifier(Text);
1066 Parsed.push_back(Select);
1067 continue;
1068 }
1069 case MT_Plural: {
1070 PluralPiece *Plural = New<PluralPiece>();
1071 do {
1072 Text = Text.drop_front(); // '{' or '|'
1073 size_t End = Text.find_first_of(":");
1074 if (End == StringRef::npos)
1075 Builder.PrintFatalError("expected ':' while parsing %plural");
1076 ++End;
1077 assert(!Text.empty());
1078 Plural->OptionPrefixes.push_back(
1079 New<TextPiece>(Text.slice(0, End), "diagtext"));
1080 Text = Text.slice(End, StringRef::npos);
1081 Plural->Options.push_back(parseDiagText(Text, true));
1082 assert(!Text.empty() && "malformed %select");
1083 } while (Text.front() == '|');
1084 // Drop the trailing '}'.
1085 Text = Text.drop_front(1);
1086 Plural->Index = parseModifier(Text);
1087 Parsed.push_back(Plural);
1088 continue;
1089 }
1090 case MT_Sub: {
1091 SubstitutionPiece *Sub = New<SubstitutionPiece>();
1092 Text = Text.drop_front(); // '{'
1093 size_t NameSize = Text.find_first_of('}');
1094 assert(NameSize != size_t(-1) && "failed to find the end of the name");
1095 assert(NameSize != 0 && "empty name?");
1096 Sub->Name = Text.substr(0, NameSize).str();
1097 Text = Text.drop_front(NameSize);
1098 Text = Text.drop_front(); // '}'
1099 if (!Text.empty()) {
1100 while (true) {
1101 if (!isdigit(Text[0]))
1102 break;
1103 Sub->Modifiers.push_back(parseModifier(Text));
1104 if (Text.empty() || Text[0] != ',')
1105 break;
1106 Text = Text.drop_front(); // ','
1107 assert(!Text.empty() && isdigit(Text[0]) &&
1108 "expected another modifier");
1109 }
1110 }
1111 Parsed.push_back(Sub);
1112 continue;
1113 }
1114 case MT_Diff: {
1115 DiffPiece *Diff = New<DiffPiece>();
1116 Text = Text.drop_front(); // '{'
1117 Diff->Options[0] = parseDiagText(Text, true);
1118 Text = Text.drop_front(); // '|'
1119 Diff->Options[1] = parseDiagText(Text, true);
1120
1121 Text = Text.drop_front(); // '}'
1122 Diff->Indexes[0] = parseModifier(Text);
1123 Text = Text.drop_front(); // ','
1124 Diff->Indexes[1] = parseModifier(Text);
1125 Parsed.push_back(Diff);
1126 continue;
1127 }
1128 case MT_S: {
1129 SelectPiece *Select = New<SelectPiece>(ModType);
1130 Select->Options.push_back(New<TextPiece>(""));
1131 Select->Options.push_back(New<TextPiece>("s", "diagtext"));
1132 Select->Index = parseModifier(Text);
1133 Parsed.push_back(Select);
1134 continue;
1135 }
1136 case MT_Q:
1137 case MT_Placeholder:
1138 case MT_ObjCClass:
1139 case MT_ObjCInstance:
1140 case MT_Ordinal: {
1141 Parsed.push_back(New<PlaceholderPiece>(ModType, parseModifier(Text)));
1142 continue;
1143 }
1144 }
1145 }
1146
1147 return New<MultiPiece>(Parsed);
1148 }
1149
1150 std::vector<std::string>
buildForDocumentation(StringRef Severity,const Record * R)1151 DiagnosticTextBuilder::buildForDocumentation(StringRef Severity,
1152 const Record *R) {
1153 EvaluatingRecordGuard Guard(&EvaluatingRecord, R);
1154 StringRef Text = R->getValueAsString("Text");
1155
1156 DiagText D(*this, Text);
1157 TextPiece *Prefix = D.New<TextPiece>(Severity, Severity);
1158 Prefix->Text += ": ";
1159 auto *MP = dyn_cast<MultiPiece>(D.Root);
1160 if (!MP) {
1161 MP = D.New<MultiPiece>();
1162 MP->Pieces.push_back(D.Root);
1163 D.Root = MP;
1164 }
1165 MP->Pieces.insert(MP->Pieces.begin(), Prefix);
1166 std::vector<std::string> Result;
1167 DiagTextDocPrinter{*this, Result}.Visit(D.Root);
1168 return Result;
1169 }
1170
buildForDefinition(const Record * R)1171 std::string DiagnosticTextBuilder::buildForDefinition(const Record *R) {
1172 EvaluatingRecordGuard Guard(&EvaluatingRecord, R);
1173 StringRef Text = R->getValueAsString("Text");
1174 DiagText D(*this, Text);
1175 std::string Result;
1176 DiagTextPrinter{*this, Result}.Visit(D.Root);
1177 return Result;
1178 }
1179
1180 } // namespace
1181
1182 //===----------------------------------------------------------------------===//
1183 // Warning Tables (.inc file) generation.
1184 //===----------------------------------------------------------------------===//
1185
isError(const Record & Diag)1186 static bool isError(const Record &Diag) {
1187 const std::string &ClsName =
1188 std::string(Diag.getValueAsDef("Class")->getName());
1189 return ClsName == "CLASS_ERROR";
1190 }
1191
isRemark(const Record & Diag)1192 static bool isRemark(const Record &Diag) {
1193 const std::string &ClsName =
1194 std::string(Diag.getValueAsDef("Class")->getName());
1195 return ClsName == "CLASS_REMARK";
1196 }
1197
1198
1199 /// ClangDiagsDefsEmitter - The top-level class emits .def files containing
1200 /// declarations of Clang diagnostics.
EmitClangDiagsDefs(RecordKeeper & Records,raw_ostream & OS,const std::string & Component)1201 void clang::EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS,
1202 const std::string &Component) {
1203 // Write the #if guard
1204 if (!Component.empty()) {
1205 std::string ComponentName = StringRef(Component).upper();
1206 OS << "#ifdef " << ComponentName << "START\n";
1207 OS << "__" << ComponentName << "START = DIAG_START_" << ComponentName
1208 << ",\n";
1209 OS << "#undef " << ComponentName << "START\n";
1210 OS << "#endif\n\n";
1211 }
1212
1213 DiagnosticTextBuilder DiagTextBuilder(Records);
1214
1215 std::vector<Record *> Diags = Records.getAllDerivedDefinitions("Diagnostic");
1216
1217 std::vector<Record*> DiagGroups
1218 = Records.getAllDerivedDefinitions("DiagGroup");
1219
1220 std::map<std::string, GroupInfo> DiagsInGroup;
1221 groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
1222
1223 DiagCategoryIDMap CategoryIDs(Records);
1224 DiagGroupParentMap DGParentMap(Records);
1225
1226 // Compute the set of diagnostics that are in -Wpedantic.
1227 RecordSet DiagsInPedantic;
1228 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
1229 inferPedantic.compute(&DiagsInPedantic, (RecordVec*)nullptr);
1230
1231 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
1232 const Record &R = *Diags[i];
1233
1234 // Check if this is an error that is accidentally in a warning
1235 // group.
1236 if (isError(R)) {
1237 if (DefInit *Group = dyn_cast<DefInit>(R.getValueInit("Group"))) {
1238 const Record *GroupRec = Group->getDef();
1239 const std::string &GroupName =
1240 std::string(GroupRec->getValueAsString("GroupName"));
1241 PrintFatalError(R.getLoc(), "Error " + R.getName() +
1242 " cannot be in a warning group [" + GroupName + "]");
1243 }
1244 }
1245
1246 // Check that all remarks have an associated diagnostic group.
1247 if (isRemark(R)) {
1248 if (!isa<DefInit>(R.getValueInit("Group"))) {
1249 PrintFatalError(R.getLoc(), "Error " + R.getName() +
1250 " not in any diagnostic group");
1251 }
1252 }
1253
1254 // Filter by component.
1255 if (!Component.empty() && Component != R.getValueAsString("Component"))
1256 continue;
1257
1258 OS << "DIAG(" << R.getName() << ", ";
1259 OS << R.getValueAsDef("Class")->getName();
1260 OS << ", (unsigned)diag::Severity::"
1261 << R.getValueAsDef("DefaultSeverity")->getValueAsString("Name");
1262
1263 // Description string.
1264 OS << ", \"";
1265 OS.write_escaped(DiagTextBuilder.buildForDefinition(&R)) << '"';
1266
1267 // Warning associated with the diagnostic. This is stored as an index into
1268 // the alphabetically sorted warning table.
1269 if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) {
1270 std::map<std::string, GroupInfo>::iterator I = DiagsInGroup.find(
1271 std::string(DI->getDef()->getValueAsString("GroupName")));
1272 assert(I != DiagsInGroup.end());
1273 OS << ", " << I->second.IDNo;
1274 } else if (DiagsInPedantic.count(&R)) {
1275 std::map<std::string, GroupInfo>::iterator I =
1276 DiagsInGroup.find("pedantic");
1277 assert(I != DiagsInGroup.end() && "pedantic group not defined");
1278 OS << ", " << I->second.IDNo;
1279 } else {
1280 OS << ", 0";
1281 }
1282
1283 // SFINAE response.
1284 OS << ", " << R.getValueAsDef("SFINAE")->getName();
1285
1286 // Default warning has no Werror bit.
1287 if (R.getValueAsBit("WarningNoWerror"))
1288 OS << ", true";
1289 else
1290 OS << ", false";
1291
1292 if (R.getValueAsBit("ShowInSystemHeader"))
1293 OS << ", true";
1294 else
1295 OS << ", false";
1296
1297 if (R.getValueAsBit("Deferrable"))
1298 OS << ", true";
1299 else
1300 OS << ", false";
1301
1302 // Category number.
1303 OS << ", " << CategoryIDs.getID(getDiagnosticCategory(&R, DGParentMap));
1304 OS << ")\n";
1305 }
1306 }
1307
1308 //===----------------------------------------------------------------------===//
1309 // Warning Group Tables generation
1310 //===----------------------------------------------------------------------===//
1311
getDiagCategoryEnum(llvm::StringRef name)1312 static std::string getDiagCategoryEnum(llvm::StringRef name) {
1313 if (name.empty())
1314 return "DiagCat_None";
1315 SmallString<256> enumName = llvm::StringRef("DiagCat_");
1316 for (llvm::StringRef::iterator I = name.begin(), E = name.end(); I != E; ++I)
1317 enumName += isalnum(*I) ? *I : '_';
1318 return std::string(enumName.str());
1319 }
1320
1321 /// Emit the array of diagnostic subgroups.
1322 ///
1323 /// The array of diagnostic subgroups contains for each group a list of its
1324 /// subgroups. The individual lists are separated by '-1'. Groups with no
1325 /// subgroups are skipped.
1326 ///
1327 /// \code
1328 /// static const int16_t DiagSubGroups[] = {
1329 /// /* Empty */ -1,
1330 /// /* DiagSubGroup0 */ 142, -1,
1331 /// /* DiagSubGroup13 */ 265, 322, 399, -1
1332 /// }
1333 /// \endcode
1334 ///
emitDiagSubGroups(std::map<std::string,GroupInfo> & DiagsInGroup,RecordVec & GroupsInPedantic,raw_ostream & OS)1335 static void emitDiagSubGroups(std::map<std::string, GroupInfo> &DiagsInGroup,
1336 RecordVec &GroupsInPedantic, raw_ostream &OS) {
1337 OS << "static const int16_t DiagSubGroups[] = {\n"
1338 << " /* Empty */ -1,\n";
1339 for (auto const &I : DiagsInGroup) {
1340 const bool IsPedantic = I.first == "pedantic";
1341
1342 const std::vector<std::string> &SubGroups = I.second.SubGroups;
1343 if (!SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty())) {
1344 OS << " /* DiagSubGroup" << I.second.IDNo << " */ ";
1345 for (auto const &SubGroup : SubGroups) {
1346 std::map<std::string, GroupInfo>::const_iterator RI =
1347 DiagsInGroup.find(SubGroup);
1348 assert(RI != DiagsInGroup.end() && "Referenced without existing?");
1349 OS << RI->second.IDNo << ", ";
1350 }
1351 // Emit the groups implicitly in "pedantic".
1352 if (IsPedantic) {
1353 for (auto const &Group : GroupsInPedantic) {
1354 const std::string &GroupName =
1355 std::string(Group->getValueAsString("GroupName"));
1356 std::map<std::string, GroupInfo>::const_iterator RI =
1357 DiagsInGroup.find(GroupName);
1358 assert(RI != DiagsInGroup.end() && "Referenced without existing?");
1359 OS << RI->second.IDNo << ", ";
1360 }
1361 }
1362
1363 OS << "-1,\n";
1364 }
1365 }
1366 OS << "};\n\n";
1367 }
1368
1369 /// Emit the list of diagnostic arrays.
1370 ///
1371 /// This data structure is a large array that contains itself arrays of varying
1372 /// size. Each array represents a list of diagnostics. The different arrays are
1373 /// separated by the value '-1'.
1374 ///
1375 /// \code
1376 /// static const int16_t DiagArrays[] = {
1377 /// /* Empty */ -1,
1378 /// /* DiagArray1 */ diag::warn_pragma_message,
1379 /// -1,
1380 /// /* DiagArray2 */ diag::warn_abs_too_small,
1381 /// diag::warn_unsigned_abs,
1382 /// diag::warn_wrong_absolute_value_type,
1383 /// -1
1384 /// };
1385 /// \endcode
1386 ///
emitDiagArrays(std::map<std::string,GroupInfo> & DiagsInGroup,RecordVec & DiagsInPedantic,raw_ostream & OS)1387 static void emitDiagArrays(std::map<std::string, GroupInfo> &DiagsInGroup,
1388 RecordVec &DiagsInPedantic, raw_ostream &OS) {
1389 OS << "static const int16_t DiagArrays[] = {\n"
1390 << " /* Empty */ -1,\n";
1391 for (auto const &I : DiagsInGroup) {
1392 const bool IsPedantic = I.first == "pedantic";
1393
1394 const std::vector<const Record *> &V = I.second.DiagsInGroup;
1395 if (!V.empty() || (IsPedantic && !DiagsInPedantic.empty())) {
1396 OS << " /* DiagArray" << I.second.IDNo << " */ ";
1397 for (auto *Record : V)
1398 OS << "diag::" << Record->getName() << ", ";
1399 // Emit the diagnostics implicitly in "pedantic".
1400 if (IsPedantic) {
1401 for (auto const &Diag : DiagsInPedantic)
1402 OS << "diag::" << Diag->getName() << ", ";
1403 }
1404 OS << "-1,\n";
1405 }
1406 }
1407 OS << "};\n\n";
1408 }
1409
1410 /// Emit a list of group names.
1411 ///
1412 /// This creates a long string which by itself contains a list of pascal style
1413 /// strings, which consist of a length byte directly followed by the string.
1414 ///
1415 /// \code
1416 /// static const char DiagGroupNames[] = {
1417 /// \000\020#pragma-messages\t#warnings\020CFString-literal"
1418 /// };
1419 /// \endcode
emitDiagGroupNames(StringToOffsetTable & GroupNames,raw_ostream & OS)1420 static void emitDiagGroupNames(StringToOffsetTable &GroupNames,
1421 raw_ostream &OS) {
1422 OS << "static const char DiagGroupNames[] = {\n";
1423 GroupNames.EmitString(OS);
1424 OS << "};\n\n";
1425 }
1426
1427 /// Emit diagnostic arrays and related data structures.
1428 ///
1429 /// This creates the actual diagnostic array, an array of diagnostic subgroups
1430 /// and an array of subgroup names.
1431 ///
1432 /// \code
1433 /// #ifdef GET_DIAG_ARRAYS
1434 /// static const int16_t DiagArrays[];
1435 /// static const int16_t DiagSubGroups[];
1436 /// static const char DiagGroupNames[];
1437 /// #endif
1438 /// \endcode
emitAllDiagArrays(std::map<std::string,GroupInfo> & DiagsInGroup,RecordVec & DiagsInPedantic,RecordVec & GroupsInPedantic,StringToOffsetTable & GroupNames,raw_ostream & OS)1439 static void emitAllDiagArrays(std::map<std::string, GroupInfo> &DiagsInGroup,
1440 RecordVec &DiagsInPedantic,
1441 RecordVec &GroupsInPedantic,
1442 StringToOffsetTable &GroupNames,
1443 raw_ostream &OS) {
1444 OS << "\n#ifdef GET_DIAG_ARRAYS\n";
1445 emitDiagArrays(DiagsInGroup, DiagsInPedantic, OS);
1446 emitDiagSubGroups(DiagsInGroup, GroupsInPedantic, OS);
1447 emitDiagGroupNames(GroupNames, OS);
1448 OS << "#endif // GET_DIAG_ARRAYS\n\n";
1449 }
1450
1451 /// Emit diagnostic table.
1452 ///
1453 /// The table is sorted by the name of the diagnostic group. Each element
1454 /// consists of the name of the diagnostic group (given as offset in the
1455 /// group name table), a reference to a list of diagnostics (optional) and a
1456 /// reference to a set of subgroups (optional).
1457 ///
1458 /// \code
1459 /// #ifdef GET_DIAG_TABLE
1460 /// {/* abi */ 159, /* DiagArray11 */ 19, /* Empty */ 0},
1461 /// {/* aggregate-return */ 180, /* Empty */ 0, /* Empty */ 0},
1462 /// {/* all */ 197, /* Empty */ 0, /* DiagSubGroup13 */ 3},
1463 /// {/* deprecated */ 1981,/* DiagArray1 */ 348, /* DiagSubGroup3 */ 9},
1464 /// #endif
1465 /// \endcode
emitDiagTable(std::map<std::string,GroupInfo> & DiagsInGroup,RecordVec & DiagsInPedantic,RecordVec & GroupsInPedantic,StringToOffsetTable & GroupNames,raw_ostream & OS)1466 static void emitDiagTable(std::map<std::string, GroupInfo> &DiagsInGroup,
1467 RecordVec &DiagsInPedantic,
1468 RecordVec &GroupsInPedantic,
1469 StringToOffsetTable &GroupNames, raw_ostream &OS) {
1470 unsigned MaxLen = 0;
1471
1472 for (auto const &I: DiagsInGroup)
1473 MaxLen = std::max(MaxLen, (unsigned)I.first.size());
1474
1475 OS << "\n#ifdef GET_DIAG_TABLE\n";
1476 unsigned SubGroupIndex = 1, DiagArrayIndex = 1;
1477 for (auto const &I: DiagsInGroup) {
1478 // Group option string.
1479 OS << " { /* ";
1480 if (I.first.find_first_not_of("abcdefghijklmnopqrstuvwxyz"
1481 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1482 "0123456789!@#$%^*-+=:?") !=
1483 std::string::npos)
1484 PrintFatalError("Invalid character in diagnostic group '" + I.first +
1485 "'");
1486 OS << I.first << " */ " << std::string(MaxLen - I.first.size(), ' ');
1487 // Store a pascal-style length byte at the beginning of the string.
1488 std::string Name = char(I.first.size()) + I.first;
1489 OS << GroupNames.GetOrAddStringOffset(Name, false) << ", ";
1490
1491 // Special handling for 'pedantic'.
1492 const bool IsPedantic = I.first == "pedantic";
1493
1494 // Diagnostics in the group.
1495 const std::vector<const Record *> &V = I.second.DiagsInGroup;
1496 const bool hasDiags =
1497 !V.empty() || (IsPedantic && !DiagsInPedantic.empty());
1498 if (hasDiags) {
1499 OS << "/* DiagArray" << I.second.IDNo << " */ " << DiagArrayIndex
1500 << ", ";
1501 if (IsPedantic)
1502 DiagArrayIndex += DiagsInPedantic.size();
1503 DiagArrayIndex += V.size() + 1;
1504 } else {
1505 OS << "/* Empty */ 0, ";
1506 }
1507
1508 // Subgroups.
1509 const std::vector<std::string> &SubGroups = I.second.SubGroups;
1510 const bool hasSubGroups =
1511 !SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty());
1512 if (hasSubGroups) {
1513 OS << "/* DiagSubGroup" << I.second.IDNo << " */ " << SubGroupIndex;
1514 if (IsPedantic)
1515 SubGroupIndex += GroupsInPedantic.size();
1516 SubGroupIndex += SubGroups.size() + 1;
1517 } else {
1518 OS << "/* Empty */ 0";
1519 }
1520
1521 OS << " },\n";
1522 }
1523 OS << "#endif // GET_DIAG_TABLE\n\n";
1524 }
1525
1526 /// Emit the table of diagnostic categories.
1527 ///
1528 /// The table has the form of macro calls that have two parameters. The
1529 /// category's name as well as an enum that represents the category. The
1530 /// table can be used by defining the macro 'CATEGORY' and including this
1531 /// table right after.
1532 ///
1533 /// \code
1534 /// #ifdef GET_CATEGORY_TABLE
1535 /// CATEGORY("Semantic Issue", DiagCat_Semantic_Issue)
1536 /// CATEGORY("Lambda Issue", DiagCat_Lambda_Issue)
1537 /// #endif
1538 /// \endcode
emitCategoryTable(RecordKeeper & Records,raw_ostream & OS)1539 static void emitCategoryTable(RecordKeeper &Records, raw_ostream &OS) {
1540 DiagCategoryIDMap CategoriesByID(Records);
1541 OS << "\n#ifdef GET_CATEGORY_TABLE\n";
1542 for (auto const &C : CategoriesByID)
1543 OS << "CATEGORY(\"" << C << "\", " << getDiagCategoryEnum(C) << ")\n";
1544 OS << "#endif // GET_CATEGORY_TABLE\n\n";
1545 }
1546
EmitClangDiagGroups(RecordKeeper & Records,raw_ostream & OS)1547 void clang::EmitClangDiagGroups(RecordKeeper &Records, raw_ostream &OS) {
1548 // Compute a mapping from a DiagGroup to all of its parents.
1549 DiagGroupParentMap DGParentMap(Records);
1550
1551 std::vector<Record *> Diags = Records.getAllDerivedDefinitions("Diagnostic");
1552
1553 std::vector<Record *> DiagGroups =
1554 Records.getAllDerivedDefinitions("DiagGroup");
1555
1556 std::map<std::string, GroupInfo> DiagsInGroup;
1557 groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
1558
1559 // All extensions are implicitly in the "pedantic" group. Record the
1560 // implicit set of groups in the "pedantic" group, and use this information
1561 // later when emitting the group information for Pedantic.
1562 RecordVec DiagsInPedantic;
1563 RecordVec GroupsInPedantic;
1564 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
1565 inferPedantic.compute(&DiagsInPedantic, &GroupsInPedantic);
1566
1567 StringToOffsetTable GroupNames;
1568 for (std::map<std::string, GroupInfo>::const_iterator
1569 I = DiagsInGroup.begin(),
1570 E = DiagsInGroup.end();
1571 I != E; ++I) {
1572 // Store a pascal-style length byte at the beginning of the string.
1573 std::string Name = char(I->first.size()) + I->first;
1574 GroupNames.GetOrAddStringOffset(Name, false);
1575 }
1576
1577 emitAllDiagArrays(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames,
1578 OS);
1579 emitDiagTable(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames,
1580 OS);
1581 emitCategoryTable(Records, OS);
1582 }
1583
1584 //===----------------------------------------------------------------------===//
1585 // Diagnostic name index generation
1586 //===----------------------------------------------------------------------===//
1587
1588 namespace {
1589 struct RecordIndexElement
1590 {
RecordIndexElement__anon268c2c930511::RecordIndexElement1591 RecordIndexElement() {}
RecordIndexElement__anon268c2c930511::RecordIndexElement1592 explicit RecordIndexElement(Record const &R)
1593 : Name(std::string(R.getName())) {}
1594
1595 std::string Name;
1596 };
1597 } // end anonymous namespace.
1598
EmitClangDiagsIndexName(RecordKeeper & Records,raw_ostream & OS)1599 void clang::EmitClangDiagsIndexName(RecordKeeper &Records, raw_ostream &OS) {
1600 const std::vector<Record*> &Diags =
1601 Records.getAllDerivedDefinitions("Diagnostic");
1602
1603 std::vector<RecordIndexElement> Index;
1604 Index.reserve(Diags.size());
1605 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
1606 const Record &R = *(Diags[i]);
1607 Index.push_back(RecordIndexElement(R));
1608 }
1609
1610 llvm::sort(Index,
1611 [](const RecordIndexElement &Lhs, const RecordIndexElement &Rhs) {
1612 return Lhs.Name < Rhs.Name;
1613 });
1614
1615 for (unsigned i = 0, e = Index.size(); i != e; ++i) {
1616 const RecordIndexElement &R = Index[i];
1617
1618 OS << "DIAG_NAME_INDEX(" << R.Name << ")\n";
1619 }
1620 }
1621
1622 //===----------------------------------------------------------------------===//
1623 // Diagnostic documentation generation
1624 //===----------------------------------------------------------------------===//
1625
1626 namespace docs {
1627 namespace {
1628
isRemarkGroup(const Record * DiagGroup,const std::map<std::string,GroupInfo> & DiagsInGroup)1629 bool isRemarkGroup(const Record *DiagGroup,
1630 const std::map<std::string, GroupInfo> &DiagsInGroup) {
1631 bool AnyRemarks = false, AnyNonRemarks = false;
1632
1633 std::function<void(StringRef)> Visit = [&](StringRef GroupName) {
1634 auto &GroupInfo = DiagsInGroup.find(std::string(GroupName))->second;
1635 for (const Record *Diag : GroupInfo.DiagsInGroup)
1636 (isRemark(*Diag) ? AnyRemarks : AnyNonRemarks) = true;
1637 for (const auto &Name : GroupInfo.SubGroups)
1638 Visit(Name);
1639 };
1640 Visit(DiagGroup->getValueAsString("GroupName"));
1641
1642 if (AnyRemarks && AnyNonRemarks)
1643 PrintFatalError(
1644 DiagGroup->getLoc(),
1645 "Diagnostic group contains both remark and non-remark diagnostics");
1646 return AnyRemarks;
1647 }
1648
getDefaultSeverity(const Record * Diag)1649 std::string getDefaultSeverity(const Record *Diag) {
1650 return std::string(
1651 Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name"));
1652 }
1653
1654 std::set<std::string>
getDefaultSeverities(const Record * DiagGroup,const std::map<std::string,GroupInfo> & DiagsInGroup)1655 getDefaultSeverities(const Record *DiagGroup,
1656 const std::map<std::string, GroupInfo> &DiagsInGroup) {
1657 std::set<std::string> States;
1658
1659 std::function<void(StringRef)> Visit = [&](StringRef GroupName) {
1660 auto &GroupInfo = DiagsInGroup.find(std::string(GroupName))->second;
1661 for (const Record *Diag : GroupInfo.DiagsInGroup)
1662 States.insert(getDefaultSeverity(Diag));
1663 for (const auto &Name : GroupInfo.SubGroups)
1664 Visit(Name);
1665 };
1666 Visit(DiagGroup->getValueAsString("GroupName"));
1667 return States;
1668 }
1669
writeHeader(StringRef Str,raw_ostream & OS,char Kind='-')1670 void writeHeader(StringRef Str, raw_ostream &OS, char Kind = '-') {
1671 OS << Str << "\n" << std::string(Str.size(), Kind) << "\n";
1672 }
1673
writeDiagnosticText(DiagnosticTextBuilder & Builder,const Record * R,StringRef Role,raw_ostream & OS)1674 void writeDiagnosticText(DiagnosticTextBuilder &Builder, const Record *R,
1675 StringRef Role, raw_ostream &OS) {
1676 StringRef Text = R->getValueAsString("Text");
1677 if (Text == "%0")
1678 OS << "The text of this diagnostic is not controlled by Clang.\n\n";
1679 else {
1680 std::vector<std::string> Out = Builder.buildForDocumentation(Role, R);
1681 for (auto &Line : Out)
1682 OS << Line << "\n";
1683 OS << "\n";
1684 }
1685 }
1686
1687 } // namespace
1688 } // namespace docs
1689
EmitClangDiagDocs(RecordKeeper & Records,raw_ostream & OS)1690 void clang::EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS) {
1691 using namespace docs;
1692
1693 // Get the documentation introduction paragraph.
1694 const Record *Documentation = Records.getDef("GlobalDocumentation");
1695 if (!Documentation) {
1696 PrintFatalError("The Documentation top-level definition is missing, "
1697 "no documentation will be generated.");
1698 return;
1699 }
1700
1701 OS << Documentation->getValueAsString("Intro") << "\n";
1702
1703 DiagnosticTextBuilder Builder(Records);
1704
1705 std::vector<Record*> Diags =
1706 Records.getAllDerivedDefinitions("Diagnostic");
1707
1708 std::vector<Record*> DiagGroups =
1709 Records.getAllDerivedDefinitions("DiagGroup");
1710 llvm::sort(DiagGroups, diagGroupBeforeByName);
1711
1712 DiagGroupParentMap DGParentMap(Records);
1713
1714 std::map<std::string, GroupInfo> DiagsInGroup;
1715 groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
1716
1717 // Compute the set of diagnostics that are in -Wpedantic.
1718 {
1719 RecordSet DiagsInPedanticSet;
1720 RecordSet GroupsInPedanticSet;
1721 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
1722 inferPedantic.compute(&DiagsInPedanticSet, &GroupsInPedanticSet);
1723 auto &PedDiags = DiagsInGroup["pedantic"];
1724 // Put the diagnostics into a deterministic order.
1725 RecordVec DiagsInPedantic(DiagsInPedanticSet.begin(),
1726 DiagsInPedanticSet.end());
1727 RecordVec GroupsInPedantic(GroupsInPedanticSet.begin(),
1728 GroupsInPedanticSet.end());
1729 llvm::sort(DiagsInPedantic, beforeThanCompare);
1730 llvm::sort(GroupsInPedantic, beforeThanCompare);
1731 PedDiags.DiagsInGroup.insert(PedDiags.DiagsInGroup.end(),
1732 DiagsInPedantic.begin(),
1733 DiagsInPedantic.end());
1734 for (auto *Group : GroupsInPedantic)
1735 PedDiags.SubGroups.push_back(
1736 std::string(Group->getValueAsString("GroupName")));
1737 }
1738
1739 // FIXME: Write diagnostic categories and link to diagnostic groups in each.
1740
1741 // Write out the diagnostic groups.
1742 for (const Record *G : DiagGroups) {
1743 bool IsRemarkGroup = isRemarkGroup(G, DiagsInGroup);
1744 auto &GroupInfo =
1745 DiagsInGroup[std::string(G->getValueAsString("GroupName"))];
1746 bool IsSynonym = GroupInfo.DiagsInGroup.empty() &&
1747 GroupInfo.SubGroups.size() == 1;
1748
1749 writeHeader(((IsRemarkGroup ? "-R" : "-W") +
1750 G->getValueAsString("GroupName")).str(),
1751 OS);
1752
1753 if (!IsSynonym) {
1754 // FIXME: Ideally, all the diagnostics in a group should have the same
1755 // default state, but that is not currently the case.
1756 auto DefaultSeverities = getDefaultSeverities(G, DiagsInGroup);
1757 if (!DefaultSeverities.empty() && !DefaultSeverities.count("Ignored")) {
1758 bool AnyNonErrors = DefaultSeverities.count("Warning") ||
1759 DefaultSeverities.count("Remark");
1760 if (!AnyNonErrors)
1761 OS << "This diagnostic is an error by default, but the flag ``-Wno-"
1762 << G->getValueAsString("GroupName") << "`` can be used to disable "
1763 << "the error.\n\n";
1764 else
1765 OS << "This diagnostic is enabled by default.\n\n";
1766 } else if (DefaultSeverities.size() > 1) {
1767 OS << "Some of the diagnostics controlled by this flag are enabled "
1768 << "by default.\n\n";
1769 }
1770 }
1771
1772 if (!GroupInfo.SubGroups.empty()) {
1773 if (IsSynonym)
1774 OS << "Synonym for ";
1775 else if (GroupInfo.DiagsInGroup.empty())
1776 OS << "Controls ";
1777 else
1778 OS << "Also controls ";
1779
1780 bool First = true;
1781 llvm::sort(GroupInfo.SubGroups);
1782 for (const auto &Name : GroupInfo.SubGroups) {
1783 if (!First) OS << ", ";
1784 OS << "`" << (IsRemarkGroup ? "-R" : "-W") << Name << "`_";
1785 First = false;
1786 }
1787 OS << ".\n\n";
1788 }
1789
1790 if (!GroupInfo.DiagsInGroup.empty()) {
1791 OS << "**Diagnostic text:**\n\n";
1792 for (const Record *D : GroupInfo.DiagsInGroup) {
1793 auto Severity = getDefaultSeverity(D);
1794 Severity[0] = tolower(Severity[0]);
1795 if (Severity == "ignored")
1796 Severity = IsRemarkGroup ? "remark" : "warning";
1797
1798 writeDiagnosticText(Builder, D, Severity, OS);
1799 }
1800 }
1801
1802 auto Doc = G->getValueAsString("Documentation");
1803 if (!Doc.empty())
1804 OS << Doc;
1805 else if (GroupInfo.SubGroups.empty() && GroupInfo.DiagsInGroup.empty())
1806 OS << "This diagnostic flag exists for GCC compatibility, and has no "
1807 "effect in Clang.\n";
1808 OS << "\n";
1809 }
1810 }
1811