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/PointerUnion.h"
16 #include "llvm/ADT/STLExtras.h"
17 #include "llvm/ADT/SmallPtrSet.h"
18 #include "llvm/ADT/SmallString.h"
19 #include "llvm/ADT/SmallVector.h"
20 #include "llvm/ADT/StringMap.h"
21 #include "llvm/ADT/StringSwitch.h"
22 #include "llvm/ADT/Twine.h"
23 #include "llvm/Support/Casting.h"
24 #include "llvm/TableGen/Error.h"
25 #include "llvm/TableGen/Record.h"
26 #include "llvm/TableGen/StringToOffsetTable.h"
27 #include "llvm/TableGen/TableGenBackend.h"
28 #include <algorithm>
29 #include <cctype>
30 #include <functional>
31 #include <map>
32 #include <optional>
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 llvm::StringRef GroupName;
133 std::vector<const Record*> DiagsInGroup;
134 std::vector<std::string> SubGroups;
135 unsigned IDNo;
136
137 llvm::SmallVector<const Record *, 1> Defs;
138
GroupInfo__anon7dbd0d690211::GroupInfo139 GroupInfo() : IDNo(0) {}
140 };
141 } // end anonymous namespace.
142
beforeThanCompare(const Record * LHS,const Record * RHS)143 static bool beforeThanCompare(const Record *LHS, const Record *RHS) {
144 assert(!LHS->getLoc().empty() && !RHS->getLoc().empty());
145 return
146 LHS->getLoc().front().getPointer() < RHS->getLoc().front().getPointer();
147 }
148
diagGroupBeforeByName(const Record * LHS,const Record * RHS)149 static bool diagGroupBeforeByName(const Record *LHS, const Record *RHS) {
150 return LHS->getValueAsString("GroupName") <
151 RHS->getValueAsString("GroupName");
152 }
153
154 /// Invert the 1-[0/1] mapping of diags to group into a one to many
155 /// 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)156 static void groupDiagnostics(const std::vector<Record*> &Diags,
157 const std::vector<Record*> &DiagGroups,
158 std::map<std::string, GroupInfo> &DiagsInGroup) {
159
160 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
161 const Record *R = Diags[i];
162 DefInit *DI = dyn_cast<DefInit>(R->getValueInit("Group"));
163 if (!DI)
164 continue;
165 assert(R->getValueAsDef("Class")->getName() != "CLASS_NOTE" &&
166 "Note can't be in a DiagGroup");
167 std::string GroupName =
168 std::string(DI->getDef()->getValueAsString("GroupName"));
169 DiagsInGroup[GroupName].DiagsInGroup.push_back(R);
170 }
171
172 // Add all DiagGroup's to the DiagsInGroup list to make sure we pick up empty
173 // groups (these are warnings that GCC supports that clang never produces).
174 for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) {
175 Record *Group = DiagGroups[i];
176 GroupInfo &GI =
177 DiagsInGroup[std::string(Group->getValueAsString("GroupName"))];
178 GI.GroupName = Group->getName();
179 GI.Defs.push_back(Group);
180
181 std::vector<Record*> SubGroups = Group->getValueAsListOfDefs("SubGroups");
182 for (unsigned j = 0, e = SubGroups.size(); j != e; ++j)
183 GI.SubGroups.push_back(
184 std::string(SubGroups[j]->getValueAsString("GroupName")));
185 }
186
187 // Assign unique ID numbers to the groups.
188 unsigned IDNo = 0;
189 for (std::map<std::string, GroupInfo>::iterator
190 I = DiagsInGroup.begin(), E = DiagsInGroup.end(); I != E; ++I, ++IDNo)
191 I->second.IDNo = IDNo;
192
193 // Warn if the same group is defined more than once (including implicitly).
194 for (auto &Group : DiagsInGroup) {
195 if (Group.second.Defs.size() == 1 &&
196 (!Group.second.Defs.front()->isAnonymous() ||
197 Group.second.DiagsInGroup.size() <= 1))
198 continue;
199
200 bool First = true;
201 for (const Record *Def : Group.second.Defs) {
202 // Skip implicit definitions from diagnostics; we'll report those
203 // separately below.
204 bool IsImplicit = false;
205 for (const Record *Diag : Group.second.DiagsInGroup) {
206 if (cast<DefInit>(Diag->getValueInit("Group"))->getDef() == Def) {
207 IsImplicit = true;
208 break;
209 }
210 }
211 if (IsImplicit)
212 continue;
213
214 llvm::SMLoc Loc = Def->getLoc().front();
215 if (First) {
216 SrcMgr.PrintMessage(Loc, SourceMgr::DK_Error,
217 Twine("group '") + Group.first +
218 "' is defined more than once");
219 First = false;
220 } else {
221 SrcMgr.PrintMessage(Loc, SourceMgr::DK_Note, "also defined here");
222 }
223 }
224
225 for (const Record *Diag : Group.second.DiagsInGroup) {
226 if (!cast<DefInit>(Diag->getValueInit("Group"))->getDef()->isAnonymous())
227 continue;
228
229 llvm::SMLoc Loc = Diag->getLoc().front();
230 if (First) {
231 SrcMgr.PrintMessage(Loc, SourceMgr::DK_Error,
232 Twine("group '") + Group.first +
233 "' is implicitly defined more than once");
234 First = false;
235 } else {
236 SrcMgr.PrintMessage(Loc, SourceMgr::DK_Note,
237 "also implicitly defined here");
238 }
239 }
240 }
241 }
242
243 //===----------------------------------------------------------------------===//
244 // Infer members of -Wpedantic.
245 //===----------------------------------------------------------------------===//
246
247 typedef std::vector<const Record *> RecordVec;
248 typedef llvm::DenseSet<const Record *> RecordSet;
249 typedef llvm::PointerUnion<RecordVec*, RecordSet*> VecOrSet;
250
251 namespace {
252 class InferPedantic {
253 typedef llvm::DenseMap<const Record *,
254 std::pair<unsigned, std::optional<unsigned>>>
255 GMap;
256
257 DiagGroupParentMap &DiagGroupParents;
258 const std::vector<Record*> &Diags;
259 const std::vector<Record*> DiagGroups;
260 std::map<std::string, GroupInfo> &DiagsInGroup;
261 llvm::DenseSet<const Record*> DiagsSet;
262 GMap GroupCount;
263 public:
InferPedantic(DiagGroupParentMap & DiagGroupParents,const std::vector<Record * > & Diags,const std::vector<Record * > & DiagGroups,std::map<std::string,GroupInfo> & DiagsInGroup)264 InferPedantic(DiagGroupParentMap &DiagGroupParents,
265 const std::vector<Record*> &Diags,
266 const std::vector<Record*> &DiagGroups,
267 std::map<std::string, GroupInfo> &DiagsInGroup)
268 : DiagGroupParents(DiagGroupParents),
269 Diags(Diags),
270 DiagGroups(DiagGroups),
271 DiagsInGroup(DiagsInGroup) {}
272
273 /// Compute the set of diagnostics and groups that are immediately
274 /// in -Wpedantic.
275 void compute(VecOrSet DiagsInPedantic,
276 VecOrSet GroupsInPedantic);
277
278 private:
279 /// Determine whether a group is a subgroup of another group.
280 bool isSubGroupOfGroup(const Record *Group,
281 llvm::StringRef RootGroupName);
282
283 /// Determine if the diagnostic is an extension.
284 bool isExtension(const Record *Diag);
285
286 /// Determine if the diagnostic is off by default.
287 bool isOffByDefault(const Record *Diag);
288
289 /// Increment the count for a group, and transitively marked
290 /// parent groups when appropriate.
291 void markGroup(const Record *Group);
292
293 /// Return true if the diagnostic is in a pedantic group.
294 bool groupInPedantic(const Record *Group, bool increment = false);
295 };
296 } // end anonymous namespace
297
isSubGroupOfGroup(const Record * Group,llvm::StringRef GName)298 bool InferPedantic::isSubGroupOfGroup(const Record *Group,
299 llvm::StringRef GName) {
300 const std::string &GroupName =
301 std::string(Group->getValueAsString("GroupName"));
302 if (GName == GroupName)
303 return true;
304
305 const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
306 for (unsigned i = 0, e = Parents.size(); i != e; ++i)
307 if (isSubGroupOfGroup(Parents[i], GName))
308 return true;
309
310 return false;
311 }
312
313 /// Determine if the diagnostic is an extension.
isExtension(const Record * Diag)314 bool InferPedantic::isExtension(const Record *Diag) {
315 const std::string &ClsName =
316 std::string(Diag->getValueAsDef("Class")->getName());
317 return ClsName == "CLASS_EXTENSION";
318 }
319
isOffByDefault(const Record * Diag)320 bool InferPedantic::isOffByDefault(const Record *Diag) {
321 const std::string &DefSeverity = std::string(
322 Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name"));
323 return DefSeverity == "Ignored";
324 }
325
groupInPedantic(const Record * Group,bool increment)326 bool InferPedantic::groupInPedantic(const Record *Group, bool increment) {
327 GMap::mapped_type &V = GroupCount[Group];
328 // Lazily compute the threshold value for the group count.
329 if (!V.second) {
330 const GroupInfo &GI =
331 DiagsInGroup[std::string(Group->getValueAsString("GroupName"))];
332 V.second = GI.SubGroups.size() + GI.DiagsInGroup.size();
333 }
334
335 if (increment)
336 ++V.first;
337
338 // Consider a group in -Wpendatic IFF if has at least one diagnostic
339 // or subgroup AND all of those diagnostics and subgroups are covered
340 // by -Wpedantic via our computation.
341 return V.first != 0 && V.first == *V.second;
342 }
343
markGroup(const Record * Group)344 void InferPedantic::markGroup(const Record *Group) {
345 // If all the diagnostics and subgroups have been marked as being
346 // covered by -Wpedantic, increment the count of parent groups. Once the
347 // group's count is equal to the number of subgroups and diagnostics in
348 // that group, we can safely add this group to -Wpedantic.
349 if (groupInPedantic(Group, /* increment */ true)) {
350 const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
351 for (unsigned i = 0, e = Parents.size(); i != e; ++i)
352 markGroup(Parents[i]);
353 }
354 }
355
compute(VecOrSet DiagsInPedantic,VecOrSet GroupsInPedantic)356 void InferPedantic::compute(VecOrSet DiagsInPedantic,
357 VecOrSet GroupsInPedantic) {
358 // All extensions that are not on by default are implicitly in the
359 // "pedantic" group. For those that aren't explicitly included in -Wpedantic,
360 // mark them for consideration to be included in -Wpedantic directly.
361 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
362 Record *R = Diags[i];
363 if (isExtension(R) && isOffByDefault(R)) {
364 DiagsSet.insert(R);
365 if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) {
366 const Record *GroupRec = Group->getDef();
367 if (!isSubGroupOfGroup(GroupRec, "pedantic")) {
368 markGroup(GroupRec);
369 }
370 }
371 }
372 }
373
374 // Compute the set of diagnostics that are directly in -Wpedantic. We
375 // march through Diags a second time to ensure the results are emitted
376 // in deterministic order.
377 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
378 Record *R = Diags[i];
379 if (!DiagsSet.count(R))
380 continue;
381 // Check if the group is implicitly in -Wpedantic. If so,
382 // the diagnostic should not be directly included in the -Wpedantic
383 // diagnostic group.
384 if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group")))
385 if (groupInPedantic(Group->getDef()))
386 continue;
387
388 // The diagnostic is not included in a group that is (transitively) in
389 // -Wpedantic. Include it in -Wpedantic directly.
390 if (RecordVec *V = DiagsInPedantic.dyn_cast<RecordVec*>())
391 V->push_back(R);
392 else {
393 DiagsInPedantic.get<RecordSet*>()->insert(R);
394 }
395 }
396
397 if (!GroupsInPedantic)
398 return;
399
400 // Compute the set of groups that are directly in -Wpedantic. We
401 // march through the groups to ensure the results are emitted
402 /// in a deterministc order.
403 for (unsigned i = 0, ei = DiagGroups.size(); i != ei; ++i) {
404 Record *Group = DiagGroups[i];
405 if (!groupInPedantic(Group))
406 continue;
407
408 const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
409 bool AllParentsInPedantic =
410 llvm::all_of(Parents, [&](Record *R) { return groupInPedantic(R); });
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 && AllParentsInPedantic)
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__anon7dbd0d690511::Piece482 Piece(PieceKind Kind) : ClassKind(Kind) {}
483 Piece(Piece const &O) = delete;
484 Piece &operator=(Piece const &) = delete;
~Piece__anon7dbd0d690511::Piece485 virtual ~Piece() {}
486
getPieceClass__anon7dbd0d690511::Piece487 PieceKind getPieceClass() const { return ClassKind; }
classof__anon7dbd0d690511::Piece488 static bool classof(const Piece *) { return true; }
489
490 private:
491 PieceKind ClassKind;
492 };
493
494 struct MultiPiece : Piece {
MultiPiece__anon7dbd0d690511::MultiPiece495 MultiPiece() : Piece(MultiPieceClass) {}
MultiPiece__anon7dbd0d690511::MultiPiece496 MultiPiece(std::vector<Piece *> Pieces)
497 : Piece(MultiPieceClass), Pieces(std::move(Pieces)) {}
498
499 std::vector<Piece *> Pieces;
500
classof__anon7dbd0d690511::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__anon7dbd0d690511::TextPiece509 TextPiece(StringRef Text, StringRef Role = "")
510 : Piece(TextPieceClass), Role(Role), Text(Text.str()) {}
511
classof__anon7dbd0d690511::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__anon7dbd0d690511::PlaceholderPiece520 PlaceholderPiece(ModifierType Kind, int Index)
521 : Piece(PlaceholderPieceClass), Kind(Kind), Index(Index) {}
522
classof__anon7dbd0d690511::PlaceholderPiece523 static bool classof(const Piece *P) {
524 return P->getPieceClass() == PlaceholderPieceClass;
525 }
526 };
527
528 struct SelectPiece : Piece {
529 protected:
SelectPiece__anon7dbd0d690511::SelectPiece530 SelectPiece(PieceKind Kind, ModifierType ModKind)
531 : Piece(Kind), ModKind(ModKind) {}
532
533 public:
SelectPiece__anon7dbd0d690511::SelectPiece534 SelectPiece(ModifierType ModKind) : SelectPiece(SelectPieceClass, ModKind) {}
535
536 ModifierType ModKind;
537 std::vector<Piece *> Options;
538 int Index = 0;
539
classof__anon7dbd0d690511::SelectPiece540 static bool classof(const Piece *P) {
541 return P->getPieceClass() == SelectPieceClass ||
542 P->getPieceClass() == PluralPieceClass;
543 }
544 };
545
546 struct PluralPiece : SelectPiece {
PluralPiece__anon7dbd0d690511::PluralPiece547 PluralPiece() : SelectPiece(PluralPieceClass, MT_Plural) {}
548
549 std::vector<Piece *> OptionPrefixes;
550 int Index = 0;
551
classof__anon7dbd0d690511::PluralPiece552 static bool classof(const Piece *P) {
553 return P->getPieceClass() == PluralPieceClass;
554 }
555 };
556
557 struct DiffPiece : Piece {
DiffPiece__anon7dbd0d690511::DiffPiece558 DiffPiece() : Piece(DiffPieceClass) {}
559
560 Piece *Parts[4] = {};
561 int Indexes[2] = {};
562
classof__anon7dbd0d690511::DiffPiece563 static bool classof(const Piece *P) {
564 return P->getPieceClass() == DiffPieceClass;
565 }
566 };
567
568 struct SubstitutionPiece : Piece {
SubstitutionPiece__anon7dbd0d690511::SubstitutionPiece569 SubstitutionPiece() : Piece(SubstitutionPieceClass) {}
570
571 std::string Name;
572 std::vector<int> Modifiers;
573
classof__anon7dbd0d690511::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__anon7dbd0d690511::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__anon7dbd0d690511::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__anon7dbd0d690511::DiagnosticTextBuilder617 [[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__anon7dbd0d690511::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__anon7dbd0d690511::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__anon7dbd0d690511::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__anon7dbd0d690511::DiagnosticTextBuilder::EvaluatingRecordGuard666 EvaluatingRecordGuard(const Record **Dest, const Record *New)
667 : Dest(Dest), Old(*Dest) {
668 *Dest = New;
669 }
~EvaluatingRecordGuard__anon7dbd0d690511::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 = std::optional<std::vector<int>>;
680
681 private:
getDerived__anon7dbd0d690511::DiagTextVisitor682 Derived &getDerived() { return static_cast<Derived &>(*this); }
683
684 public:
685 std::vector<int>
getSubstitutionMappings__anon7dbd0d690511::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__anon7dbd0d690511::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__anon7dbd0d690511::DiagTextVisitor::SubstitutionContext704 ~SubstitutionContext() {
705 Visitor.ModifierMappings = std::move(OldMappings);
706 }
707
708 private:
709 DiagTextVisitor &Visitor;
710 std::optional<std::vector<int>> OldMappings;
711
712 public:
713 Piece *Substitution;
714 };
715
716 public:
DiagTextVisitor__anon7dbd0d690511::DiagTextVisitor717 DiagTextVisitor(DiagnosticTextBuilder &Builder) : Builder(Builder) {}
718
Visit__anon7dbd0d690511::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__anon7dbd0d690511::DiagTextVisitor735 void VisitSubstitution(SubstitutionPiece *P) {
736 SubstitutionContext Guard(*this, P);
737 Visit(Guard.Substitution);
738 }
739
mapIndex__anon7dbd0d690511::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__anon7dbd0d690511::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__anon7dbd0d690511::DiagTextDocPrinter792 DiagTextDocPrinter(DiagnosticTextBuilder &Builder,
793 std::vector<std::string> &RST)
794 : BaseTy(Builder), RST(RST) {}
795
gatherNodes__anon7dbd0d690511::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__anon7dbd0d690511::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__anon7dbd0d690511::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__anon7dbd0d690511::DiagTextDocPrinter890 void VisitPlaceholder(PlaceholderPiece *P) {
891 RST.push_back(std::string(":placeholder:`") +
892 char('A' + mapIndex(P->Index)) + "`");
893 }
894
VisitSelect__anon7dbd0d690511::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__anon7dbd0d690511::DiagTextDocPrinter911 void VisitPlural(PluralPiece *P) { VisitSelect(P); }
912
VisitDiff__anon7dbd0d690511::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__anon7dbd0d690511::DiagTextPrinter938 DiagTextPrinter(DiagnosticTextBuilder &Builder, std::string &Result)
939 : BaseTy(Builder), Result(Result) {}
940
VisitMulti__anon7dbd0d690511::DiagTextPrinter941 void VisitMulti(MultiPiece *P) {
942 for (auto *Child : P->Pieces)
943 Visit(Child);
944 }
VisitText__anon7dbd0d690511::DiagTextPrinter945 void VisitText(TextPiece *P) { Result += P->Text; }
VisitPlaceholder__anon7dbd0d690511::DiagTextPrinter946 void VisitPlaceholder(PlaceholderPiece *P) {
947 Result += "%";
948 Result += getModifierName(P->Kind);
949 addInt(mapIndex(P->Index));
950 }
VisitSelect__anon7dbd0d690511::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__anon7dbd0d690511::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__anon7dbd0d690511::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__anon7dbd0d690511::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("Summary");
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("Summary");
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 group associated with the diagnostic. This is stored as an index
1283 // into the alphabetically sorted warning group 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("ShowInSystemMacro"))
1313 OS << ", true";
1314 else
1315 OS << ", false";
1316
1317 if (R.getValueAsBit("Deferrable"))
1318 OS << ", true";
1319 else
1320 OS << ", false";
1321
1322 // Category number.
1323 OS << ", " << CategoryIDs.getID(getDiagnosticCategory(&R, DGParentMap));
1324 OS << ")\n";
1325 }
1326 }
1327
1328 //===----------------------------------------------------------------------===//
1329 // Warning Group Tables generation
1330 //===----------------------------------------------------------------------===//
1331
getDiagCategoryEnum(llvm::StringRef name)1332 static std::string getDiagCategoryEnum(llvm::StringRef name) {
1333 if (name.empty())
1334 return "DiagCat_None";
1335 SmallString<256> enumName = llvm::StringRef("DiagCat_");
1336 for (llvm::StringRef::iterator I = name.begin(), E = name.end(); I != E; ++I)
1337 enumName += isalnum(*I) ? *I : '_';
1338 return std::string(enumName.str());
1339 }
1340
1341 /// Emit the array of diagnostic subgroups.
1342 ///
1343 /// The array of diagnostic subgroups contains for each group a list of its
1344 /// subgroups. The individual lists are separated by '-1'. Groups with no
1345 /// subgroups are skipped.
1346 ///
1347 /// \code
1348 /// static const int16_t DiagSubGroups[] = {
1349 /// /* Empty */ -1,
1350 /// /* DiagSubGroup0 */ 142, -1,
1351 /// /* DiagSubGroup13 */ 265, 322, 399, -1
1352 /// }
1353 /// \endcode
1354 ///
emitDiagSubGroups(std::map<std::string,GroupInfo> & DiagsInGroup,RecordVec & GroupsInPedantic,raw_ostream & OS)1355 static void emitDiagSubGroups(std::map<std::string, GroupInfo> &DiagsInGroup,
1356 RecordVec &GroupsInPedantic, raw_ostream &OS) {
1357 OS << "static const int16_t DiagSubGroups[] = {\n"
1358 << " /* Empty */ -1,\n";
1359 for (auto const &I : DiagsInGroup) {
1360 const bool IsPedantic = I.first == "pedantic";
1361
1362 const std::vector<std::string> &SubGroups = I.second.SubGroups;
1363 if (!SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty())) {
1364 OS << " /* DiagSubGroup" << I.second.IDNo << " */ ";
1365 for (auto const &SubGroup : SubGroups) {
1366 std::map<std::string, GroupInfo>::const_iterator RI =
1367 DiagsInGroup.find(SubGroup);
1368 assert(RI != DiagsInGroup.end() && "Referenced without existing?");
1369 OS << RI->second.IDNo << ", ";
1370 }
1371 // Emit the groups implicitly in "pedantic".
1372 if (IsPedantic) {
1373 for (auto const &Group : GroupsInPedantic) {
1374 const std::string &GroupName =
1375 std::string(Group->getValueAsString("GroupName"));
1376 std::map<std::string, GroupInfo>::const_iterator RI =
1377 DiagsInGroup.find(GroupName);
1378 assert(RI != DiagsInGroup.end() && "Referenced without existing?");
1379 OS << RI->second.IDNo << ", ";
1380 }
1381 }
1382
1383 OS << "-1,\n";
1384 }
1385 }
1386 OS << "};\n\n";
1387 }
1388
1389 /// Emit the list of diagnostic arrays.
1390 ///
1391 /// This data structure is a large array that contains itself arrays of varying
1392 /// size. Each array represents a list of diagnostics. The different arrays are
1393 /// separated by the value '-1'.
1394 ///
1395 /// \code
1396 /// static const int16_t DiagArrays[] = {
1397 /// /* Empty */ -1,
1398 /// /* DiagArray1 */ diag::warn_pragma_message,
1399 /// -1,
1400 /// /* DiagArray2 */ diag::warn_abs_too_small,
1401 /// diag::warn_unsigned_abs,
1402 /// diag::warn_wrong_absolute_value_type,
1403 /// -1
1404 /// };
1405 /// \endcode
1406 ///
emitDiagArrays(std::map<std::string,GroupInfo> & DiagsInGroup,RecordVec & DiagsInPedantic,raw_ostream & OS)1407 static void emitDiagArrays(std::map<std::string, GroupInfo> &DiagsInGroup,
1408 RecordVec &DiagsInPedantic, raw_ostream &OS) {
1409 OS << "static const int16_t DiagArrays[] = {\n"
1410 << " /* Empty */ -1,\n";
1411 for (auto const &I : DiagsInGroup) {
1412 const bool IsPedantic = I.first == "pedantic";
1413
1414 const std::vector<const Record *> &V = I.second.DiagsInGroup;
1415 if (!V.empty() || (IsPedantic && !DiagsInPedantic.empty())) {
1416 OS << " /* DiagArray" << I.second.IDNo << " */ ";
1417 for (auto *Record : V)
1418 OS << "diag::" << Record->getName() << ", ";
1419 // Emit the diagnostics implicitly in "pedantic".
1420 if (IsPedantic) {
1421 for (auto const &Diag : DiagsInPedantic)
1422 OS << "diag::" << Diag->getName() << ", ";
1423 }
1424 OS << "-1,\n";
1425 }
1426 }
1427 OS << "};\n\n";
1428 }
1429
1430 /// Emit a list of group names.
1431 ///
1432 /// This creates a long string which by itself contains a list of pascal style
1433 /// strings, which consist of a length byte directly followed by the string.
1434 ///
1435 /// \code
1436 /// static const char DiagGroupNames[] = {
1437 /// \000\020#pragma-messages\t#warnings\020CFString-literal"
1438 /// };
1439 /// \endcode
emitDiagGroupNames(StringToOffsetTable & GroupNames,raw_ostream & OS)1440 static void emitDiagGroupNames(StringToOffsetTable &GroupNames,
1441 raw_ostream &OS) {
1442 OS << "static const char DiagGroupNames[] = {\n";
1443 GroupNames.EmitString(OS);
1444 OS << "};\n\n";
1445 }
1446
1447 /// Emit diagnostic arrays and related data structures.
1448 ///
1449 /// This creates the actual diagnostic array, an array of diagnostic subgroups
1450 /// and an array of subgroup names.
1451 ///
1452 /// \code
1453 /// #ifdef GET_DIAG_ARRAYS
1454 /// static const int16_t DiagArrays[];
1455 /// static const int16_t DiagSubGroups[];
1456 /// static const char DiagGroupNames[];
1457 /// #endif
1458 /// \endcode
emitAllDiagArrays(std::map<std::string,GroupInfo> & DiagsInGroup,RecordVec & DiagsInPedantic,RecordVec & GroupsInPedantic,StringToOffsetTable & GroupNames,raw_ostream & OS)1459 static void emitAllDiagArrays(std::map<std::string, GroupInfo> &DiagsInGroup,
1460 RecordVec &DiagsInPedantic,
1461 RecordVec &GroupsInPedantic,
1462 StringToOffsetTable &GroupNames,
1463 raw_ostream &OS) {
1464 OS << "\n#ifdef GET_DIAG_ARRAYS\n";
1465 emitDiagArrays(DiagsInGroup, DiagsInPedantic, OS);
1466 emitDiagSubGroups(DiagsInGroup, GroupsInPedantic, OS);
1467 emitDiagGroupNames(GroupNames, OS);
1468 OS << "#endif // GET_DIAG_ARRAYS\n\n";
1469 }
1470
1471 /// Emit diagnostic table.
1472 ///
1473 /// The table is sorted by the name of the diagnostic group. Each element
1474 /// consists of the name of the diagnostic group (given as offset in the
1475 /// group name table), a reference to a list of diagnostics (optional) and a
1476 /// reference to a set of subgroups (optional).
1477 ///
1478 /// \code
1479 /// #ifdef GET_DIAG_TABLE
1480 /// {/* abi */ 159, /* DiagArray11 */ 19, /* Empty */ 0},
1481 /// {/* aggregate-return */ 180, /* Empty */ 0, /* Empty */ 0},
1482 /// {/* all */ 197, /* Empty */ 0, /* DiagSubGroup13 */ 3},
1483 /// {/* deprecated */ 1981,/* DiagArray1 */ 348, /* DiagSubGroup3 */ 9},
1484 /// #endif
1485 /// \endcode
emitDiagTable(std::map<std::string,GroupInfo> & DiagsInGroup,RecordVec & DiagsInPedantic,RecordVec & GroupsInPedantic,StringToOffsetTable & GroupNames,raw_ostream & OS)1486 static void emitDiagTable(std::map<std::string, GroupInfo> &DiagsInGroup,
1487 RecordVec &DiagsInPedantic,
1488 RecordVec &GroupsInPedantic,
1489 StringToOffsetTable &GroupNames, raw_ostream &OS) {
1490 unsigned MaxLen = 0;
1491
1492 for (auto const &I: DiagsInGroup)
1493 MaxLen = std::max(MaxLen, (unsigned)I.first.size());
1494
1495 OS << "\n#ifdef DIAG_ENTRY\n";
1496 unsigned SubGroupIndex = 1, DiagArrayIndex = 1;
1497 for (auto const &I: DiagsInGroup) {
1498 // Group option string.
1499 OS << "DIAG_ENTRY(";
1500 OS << I.second.GroupName << " /* ";
1501
1502 if (I.first.find_first_not_of("abcdefghijklmnopqrstuvwxyz"
1503 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1504 "0123456789!@#$%^*-+=:?") !=
1505 std::string::npos)
1506 PrintFatalError("Invalid character in diagnostic group '" + I.first +
1507 "'");
1508 OS << I.first << " */, ";
1509 // Store a pascal-style length byte at the beginning of the string.
1510 std::string Name = char(I.first.size()) + I.first;
1511 OS << GroupNames.GetOrAddStringOffset(Name, false) << ", ";
1512
1513 // Special handling for 'pedantic'.
1514 const bool IsPedantic = I.first == "pedantic";
1515
1516 // Diagnostics in the group.
1517 const std::vector<const Record *> &V = I.second.DiagsInGroup;
1518 const bool hasDiags =
1519 !V.empty() || (IsPedantic && !DiagsInPedantic.empty());
1520 if (hasDiags) {
1521 OS << "/* DiagArray" << I.second.IDNo << " */ " << DiagArrayIndex
1522 << ", ";
1523 if (IsPedantic)
1524 DiagArrayIndex += DiagsInPedantic.size();
1525 DiagArrayIndex += V.size() + 1;
1526 } else {
1527 OS << "0, ";
1528 }
1529
1530 // Subgroups.
1531 const std::vector<std::string> &SubGroups = I.second.SubGroups;
1532 const bool hasSubGroups =
1533 !SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty());
1534 if (hasSubGroups) {
1535 OS << "/* DiagSubGroup" << I.second.IDNo << " */ " << SubGroupIndex
1536 << ", ";
1537 if (IsPedantic)
1538 SubGroupIndex += GroupsInPedantic.size();
1539 SubGroupIndex += SubGroups.size() + 1;
1540 } else {
1541 OS << "0, ";
1542 }
1543
1544 std::string Documentation = I.second.Defs.back()
1545 ->getValue("Documentation")
1546 ->getValue()
1547 ->getAsUnquotedString();
1548
1549 OS << "R\"(" << StringRef(Documentation).trim() << ")\"";
1550
1551 OS << ")\n";
1552 }
1553 OS << "#endif // DIAG_ENTRY\n\n";
1554 }
1555
1556 /// Emit the table of diagnostic categories.
1557 ///
1558 /// The table has the form of macro calls that have two parameters. The
1559 /// category's name as well as an enum that represents the category. The
1560 /// table can be used by defining the macro 'CATEGORY' and including this
1561 /// table right after.
1562 ///
1563 /// \code
1564 /// #ifdef GET_CATEGORY_TABLE
1565 /// CATEGORY("Semantic Issue", DiagCat_Semantic_Issue)
1566 /// CATEGORY("Lambda Issue", DiagCat_Lambda_Issue)
1567 /// #endif
1568 /// \endcode
emitCategoryTable(RecordKeeper & Records,raw_ostream & OS)1569 static void emitCategoryTable(RecordKeeper &Records, raw_ostream &OS) {
1570 DiagCategoryIDMap CategoriesByID(Records);
1571 OS << "\n#ifdef GET_CATEGORY_TABLE\n";
1572 for (auto const &C : CategoriesByID)
1573 OS << "CATEGORY(\"" << C << "\", " << getDiagCategoryEnum(C) << ")\n";
1574 OS << "#endif // GET_CATEGORY_TABLE\n\n";
1575 }
1576
EmitClangDiagGroups(RecordKeeper & Records,raw_ostream & OS)1577 void clang::EmitClangDiagGroups(RecordKeeper &Records, raw_ostream &OS) {
1578 // Compute a mapping from a DiagGroup to all of its parents.
1579 DiagGroupParentMap DGParentMap(Records);
1580
1581 std::vector<Record *> Diags = Records.getAllDerivedDefinitions("Diagnostic");
1582
1583 std::vector<Record *> DiagGroups =
1584 Records.getAllDerivedDefinitions("DiagGroup");
1585
1586 std::map<std::string, GroupInfo> DiagsInGroup;
1587 groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
1588
1589 // All extensions are implicitly in the "pedantic" group. Record the
1590 // implicit set of groups in the "pedantic" group, and use this information
1591 // later when emitting the group information for Pedantic.
1592 RecordVec DiagsInPedantic;
1593 RecordVec GroupsInPedantic;
1594 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
1595 inferPedantic.compute(&DiagsInPedantic, &GroupsInPedantic);
1596
1597 StringToOffsetTable GroupNames;
1598 for (std::map<std::string, GroupInfo>::const_iterator
1599 I = DiagsInGroup.begin(),
1600 E = DiagsInGroup.end();
1601 I != E; ++I) {
1602 // Store a pascal-style length byte at the beginning of the string.
1603 std::string Name = char(I->first.size()) + I->first;
1604 GroupNames.GetOrAddStringOffset(Name, false);
1605 }
1606
1607 emitAllDiagArrays(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames,
1608 OS);
1609 emitDiagTable(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames,
1610 OS);
1611 emitCategoryTable(Records, OS);
1612 }
1613
1614 //===----------------------------------------------------------------------===//
1615 // Diagnostic name index generation
1616 //===----------------------------------------------------------------------===//
1617
1618 namespace {
1619 struct RecordIndexElement
1620 {
RecordIndexElement__anon7dbd0d690711::RecordIndexElement1621 RecordIndexElement() {}
RecordIndexElement__anon7dbd0d690711::RecordIndexElement1622 explicit RecordIndexElement(Record const &R)
1623 : Name(std::string(R.getName())) {}
1624
1625 std::string Name;
1626 };
1627 } // end anonymous namespace.
1628
EmitClangDiagsIndexName(RecordKeeper & Records,raw_ostream & OS)1629 void clang::EmitClangDiagsIndexName(RecordKeeper &Records, raw_ostream &OS) {
1630 const std::vector<Record*> &Diags =
1631 Records.getAllDerivedDefinitions("Diagnostic");
1632
1633 std::vector<RecordIndexElement> Index;
1634 Index.reserve(Diags.size());
1635 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
1636 const Record &R = *(Diags[i]);
1637 Index.push_back(RecordIndexElement(R));
1638 }
1639
1640 llvm::sort(Index,
1641 [](const RecordIndexElement &Lhs, const RecordIndexElement &Rhs) {
1642 return Lhs.Name < Rhs.Name;
1643 });
1644
1645 for (unsigned i = 0, e = Index.size(); i != e; ++i) {
1646 const RecordIndexElement &R = Index[i];
1647
1648 OS << "DIAG_NAME_INDEX(" << R.Name << ")\n";
1649 }
1650 }
1651
1652 //===----------------------------------------------------------------------===//
1653 // Diagnostic documentation generation
1654 //===----------------------------------------------------------------------===//
1655
1656 namespace docs {
1657 namespace {
1658
isRemarkGroup(const Record * DiagGroup,const std::map<std::string,GroupInfo> & DiagsInGroup)1659 bool isRemarkGroup(const Record *DiagGroup,
1660 const std::map<std::string, GroupInfo> &DiagsInGroup) {
1661 bool AnyRemarks = false, AnyNonRemarks = false;
1662
1663 std::function<void(StringRef)> Visit = [&](StringRef GroupName) {
1664 auto &GroupInfo = DiagsInGroup.find(std::string(GroupName))->second;
1665 for (const Record *Diag : GroupInfo.DiagsInGroup)
1666 (isRemark(*Diag) ? AnyRemarks : AnyNonRemarks) = true;
1667 for (const auto &Name : GroupInfo.SubGroups)
1668 Visit(Name);
1669 };
1670 Visit(DiagGroup->getValueAsString("GroupName"));
1671
1672 if (AnyRemarks && AnyNonRemarks)
1673 PrintFatalError(
1674 DiagGroup->getLoc(),
1675 "Diagnostic group contains both remark and non-remark diagnostics");
1676 return AnyRemarks;
1677 }
1678
getDefaultSeverity(const Record * Diag)1679 std::string getDefaultSeverity(const Record *Diag) {
1680 return std::string(
1681 Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name"));
1682 }
1683
1684 std::set<std::string>
getDefaultSeverities(const Record * DiagGroup,const std::map<std::string,GroupInfo> & DiagsInGroup)1685 getDefaultSeverities(const Record *DiagGroup,
1686 const std::map<std::string, GroupInfo> &DiagsInGroup) {
1687 std::set<std::string> States;
1688
1689 std::function<void(StringRef)> Visit = [&](StringRef GroupName) {
1690 auto &GroupInfo = DiagsInGroup.find(std::string(GroupName))->second;
1691 for (const Record *Diag : GroupInfo.DiagsInGroup)
1692 States.insert(getDefaultSeverity(Diag));
1693 for (const auto &Name : GroupInfo.SubGroups)
1694 Visit(Name);
1695 };
1696 Visit(DiagGroup->getValueAsString("GroupName"));
1697 return States;
1698 }
1699
writeHeader(StringRef Str,raw_ostream & OS,char Kind='-')1700 void writeHeader(StringRef Str, raw_ostream &OS, char Kind = '-') {
1701 OS << Str << "\n" << std::string(Str.size(), Kind) << "\n";
1702 }
1703
writeDiagnosticText(DiagnosticTextBuilder & Builder,const Record * R,StringRef Role,raw_ostream & OS)1704 void writeDiagnosticText(DiagnosticTextBuilder &Builder, const Record *R,
1705 StringRef Role, raw_ostream &OS) {
1706 StringRef Text = R->getValueAsString("Summary");
1707 if (Text == "%0")
1708 OS << "The text of this diagnostic is not controlled by Clang.\n\n";
1709 else {
1710 std::vector<std::string> Out = Builder.buildForDocumentation(Role, R);
1711 for (auto &Line : Out)
1712 OS << Line << "\n";
1713 OS << "\n";
1714 }
1715 }
1716
1717 } // namespace
1718 } // namespace docs
1719
EmitClangDiagDocs(RecordKeeper & Records,raw_ostream & OS)1720 void clang::EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS) {
1721 using namespace docs;
1722
1723 // Get the documentation introduction paragraph.
1724 const Record *Documentation = Records.getDef("GlobalDocumentation");
1725 if (!Documentation) {
1726 PrintFatalError("The Documentation top-level definition is missing, "
1727 "no documentation will be generated.");
1728 return;
1729 }
1730
1731 OS << Documentation->getValueAsString("Intro") << "\n";
1732
1733 DiagnosticTextBuilder Builder(Records);
1734
1735 std::vector<Record*> Diags =
1736 Records.getAllDerivedDefinitions("Diagnostic");
1737
1738 std::vector<Record*> DiagGroups =
1739 Records.getAllDerivedDefinitions("DiagGroup");
1740 llvm::sort(DiagGroups, diagGroupBeforeByName);
1741
1742 DiagGroupParentMap DGParentMap(Records);
1743
1744 std::map<std::string, GroupInfo> DiagsInGroup;
1745 groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
1746
1747 // Compute the set of diagnostics that are in -Wpedantic.
1748 {
1749 RecordSet DiagsInPedanticSet;
1750 RecordSet GroupsInPedanticSet;
1751 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
1752 inferPedantic.compute(&DiagsInPedanticSet, &GroupsInPedanticSet);
1753 auto &PedDiags = DiagsInGroup["pedantic"];
1754 // Put the diagnostics into a deterministic order.
1755 RecordVec DiagsInPedantic(DiagsInPedanticSet.begin(),
1756 DiagsInPedanticSet.end());
1757 RecordVec GroupsInPedantic(GroupsInPedanticSet.begin(),
1758 GroupsInPedanticSet.end());
1759 llvm::sort(DiagsInPedantic, beforeThanCompare);
1760 llvm::sort(GroupsInPedantic, beforeThanCompare);
1761 PedDiags.DiagsInGroup.insert(PedDiags.DiagsInGroup.end(),
1762 DiagsInPedantic.begin(),
1763 DiagsInPedantic.end());
1764 for (auto *Group : GroupsInPedantic)
1765 PedDiags.SubGroups.push_back(
1766 std::string(Group->getValueAsString("GroupName")));
1767 }
1768
1769 // FIXME: Write diagnostic categories and link to diagnostic groups in each.
1770
1771 // Write out the diagnostic groups.
1772 for (const Record *G : DiagGroups) {
1773 bool IsRemarkGroup = isRemarkGroup(G, DiagsInGroup);
1774 auto &GroupInfo =
1775 DiagsInGroup[std::string(G->getValueAsString("GroupName"))];
1776 bool IsSynonym = GroupInfo.DiagsInGroup.empty() &&
1777 GroupInfo.SubGroups.size() == 1;
1778
1779 writeHeader(((IsRemarkGroup ? "-R" : "-W") +
1780 G->getValueAsString("GroupName")).str(),
1781 OS);
1782
1783 if (!IsSynonym) {
1784 // FIXME: Ideally, all the diagnostics in a group should have the same
1785 // default state, but that is not currently the case.
1786 auto DefaultSeverities = getDefaultSeverities(G, DiagsInGroup);
1787 if (!DefaultSeverities.empty() && !DefaultSeverities.count("Ignored")) {
1788 bool AnyNonErrors = DefaultSeverities.count("Warning") ||
1789 DefaultSeverities.count("Remark");
1790 if (!AnyNonErrors)
1791 OS << "This diagnostic is an error by default, but the flag ``-Wno-"
1792 << G->getValueAsString("GroupName") << "`` can be used to disable "
1793 << "the error.\n\n";
1794 else
1795 OS << "This diagnostic is enabled by default.\n\n";
1796 } else if (DefaultSeverities.size() > 1) {
1797 OS << "Some of the diagnostics controlled by this flag are enabled "
1798 << "by default.\n\n";
1799 }
1800 }
1801
1802 if (!GroupInfo.SubGroups.empty()) {
1803 if (IsSynonym)
1804 OS << "Synonym for ";
1805 else if (GroupInfo.DiagsInGroup.empty())
1806 OS << "Controls ";
1807 else
1808 OS << "Also controls ";
1809
1810 bool First = true;
1811 llvm::sort(GroupInfo.SubGroups);
1812 for (const auto &Name : GroupInfo.SubGroups) {
1813 if (!First) OS << ", ";
1814 OS << "`" << (IsRemarkGroup ? "-R" : "-W") << Name << "`_";
1815 First = false;
1816 }
1817 OS << ".\n\n";
1818 }
1819
1820 if (!GroupInfo.DiagsInGroup.empty()) {
1821 OS << "**Diagnostic text:**\n\n";
1822 for (const Record *D : GroupInfo.DiagsInGroup) {
1823 auto Severity = getDefaultSeverity(D);
1824 Severity[0] = tolower(Severity[0]);
1825 if (Severity == "ignored")
1826 Severity = IsRemarkGroup ? "remark" : "warning";
1827
1828 writeDiagnosticText(Builder, D, Severity, OS);
1829 }
1830 }
1831
1832 auto Doc = G->getValueAsString("Documentation");
1833 if (!Doc.empty())
1834 OS << Doc;
1835 else if (GroupInfo.SubGroups.empty() && GroupInfo.DiagsInGroup.empty())
1836 OS << "This diagnostic flag exists for GCC compatibility, and has no "
1837 "effect in Clang.\n";
1838 OS << "\n";
1839 }
1840 }
1841