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