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