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 = 0;
136
137 llvm::SmallVector<const Record *, 1> Defs;
138
139 GroupInfo() = default;
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__anonded1697c0511::Piece482 Piece(PieceKind Kind) : ClassKind(Kind) {}
483 Piece(Piece const &O) = delete;
484 Piece &operator=(Piece const &) = delete;
~Piece__anonded1697c0511::Piece485 virtual ~Piece() {}
486
getPieceClass__anonded1697c0511::Piece487 PieceKind getPieceClass() const { return ClassKind; }
classof__anonded1697c0511::Piece488 static bool classof(const Piece *) { return true; }
489
490 private:
491 PieceKind ClassKind;
492 };
493
494 struct MultiPiece : Piece {
MultiPiece__anonded1697c0511::MultiPiece495 MultiPiece() : Piece(MultiPieceClass) {}
MultiPiece__anonded1697c0511::MultiPiece496 MultiPiece(std::vector<Piece *> Pieces)
497 : Piece(MultiPieceClass), Pieces(std::move(Pieces)) {}
498
499 std::vector<Piece *> Pieces;
500
classof__anonded1697c0511::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__anonded1697c0511::TextPiece509 TextPiece(StringRef Text, StringRef Role = "")
510 : Piece(TextPieceClass), Role(Role), Text(Text.str()) {}
511
classof__anonded1697c0511::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__anonded1697c0511::PlaceholderPiece520 PlaceholderPiece(ModifierType Kind, int Index)
521 : Piece(PlaceholderPieceClass), Kind(Kind), Index(Index) {}
522
classof__anonded1697c0511::PlaceholderPiece523 static bool classof(const Piece *P) {
524 return P->getPieceClass() == PlaceholderPieceClass;
525 }
526 };
527
528 struct SelectPiece : Piece {
529 protected:
SelectPiece__anonded1697c0511::SelectPiece530 SelectPiece(PieceKind Kind, ModifierType ModKind)
531 : Piece(Kind), ModKind(ModKind) {}
532
533 public:
SelectPiece__anonded1697c0511::SelectPiece534 SelectPiece(ModifierType ModKind) : SelectPiece(SelectPieceClass, ModKind) {}
535
536 ModifierType ModKind;
537 std::vector<Piece *> Options;
538 int Index = 0;
539
classof__anonded1697c0511::SelectPiece540 static bool classof(const Piece *P) {
541 return P->getPieceClass() == SelectPieceClass ||
542 P->getPieceClass() == PluralPieceClass;
543 }
544 };
545
546 struct PluralPiece : SelectPiece {
PluralPiece__anonded1697c0511::PluralPiece547 PluralPiece() : SelectPiece(PluralPieceClass, MT_Plural) {}
548
549 std::vector<Piece *> OptionPrefixes;
550 int Index = 0;
551
classof__anonded1697c0511::PluralPiece552 static bool classof(const Piece *P) {
553 return P->getPieceClass() == PluralPieceClass;
554 }
555 };
556
557 struct DiffPiece : Piece {
DiffPiece__anonded1697c0511::DiffPiece558 DiffPiece() : Piece(DiffPieceClass) {}
559
560 Piece *Parts[4] = {};
561 int Indexes[2] = {};
562
classof__anonded1697c0511::DiffPiece563 static bool classof(const Piece *P) {
564 return P->getPieceClass() == DiffPieceClass;
565 }
566 };
567
568 struct SubstitutionPiece : Piece {
SubstitutionPiece__anonded1697c0511::SubstitutionPiece569 SubstitutionPiece() : Piece(SubstitutionPieceClass) {}
570
571 std::string Name;
572 std::vector<int> Modifiers;
573
classof__anonded1697c0511::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__anonded1697c0511::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__anonded1697c0511::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__anonded1697c0511::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
New__anonded1697c0511::DiagnosticTextBuilder::DiagText628 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__anonded1697c0511::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__anonded1697c0511::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 // The move assignment operator is defined as deleted pending further
657 // motivation.
658 DiagText &operator=(DiagText &&) = delete;
659
660 // The copy constrcutor and copy assignment operator is defined as deleted
661 // pending further motivation.
662 DiagText(const DiagText &) = delete;
663 DiagText &operator=(const DiagText &) = delete;
664
~DiagText__anonded1697c0511::DiagnosticTextBuilder::DiagText665 ~DiagText() {
666 for (Piece *P : AllocatedPieces)
667 delete P;
668 }
669 };
670
671 private:
672 const Record *EvaluatingRecord = nullptr;
673 struct EvaluatingRecordGuard {
EvaluatingRecordGuard__anonded1697c0511::DiagnosticTextBuilder::EvaluatingRecordGuard674 EvaluatingRecordGuard(const Record **Dest, const Record *New)
675 : Dest(Dest), Old(*Dest) {
676 *Dest = New;
677 }
~EvaluatingRecordGuard__anonded1697c0511::DiagnosticTextBuilder::EvaluatingRecordGuard678 ~EvaluatingRecordGuard() { *Dest = Old; }
679 const Record **Dest;
680 const Record *Old;
681 };
682
683 StringMap<DiagText> Substitutions;
684 };
685
686 template <class Derived> struct DiagTextVisitor {
687 using ModifierMappingsType = std::optional<std::vector<int>>;
688
689 private:
getDerived__anonded1697c0511::DiagTextVisitor690 Derived &getDerived() { return static_cast<Derived &>(*this); }
691
692 public:
693 std::vector<int>
getSubstitutionMappings__anonded1697c0511::DiagTextVisitor694 getSubstitutionMappings(SubstitutionPiece *P,
695 const ModifierMappingsType &Mappings) const {
696 std::vector<int> NewMappings;
697 for (int Idx : P->Modifiers)
698 NewMappings.push_back(mapIndex(Idx, Mappings));
699 return NewMappings;
700 }
701
702 struct SubstitutionContext {
SubstitutionContext__anonded1697c0511::DiagTextVisitor::SubstitutionContext703 SubstitutionContext(DiagTextVisitor &Visitor, SubstitutionPiece *P)
704 : Visitor(Visitor) {
705 Substitution = Visitor.Builder.getSubstitution(P);
706 OldMappings = std::move(Visitor.ModifierMappings);
707 std::vector<int> NewMappings =
708 Visitor.getSubstitutionMappings(P, OldMappings);
709 Visitor.ModifierMappings = std::move(NewMappings);
710 }
711
~SubstitutionContext__anonded1697c0511::DiagTextVisitor::SubstitutionContext712 ~SubstitutionContext() {
713 Visitor.ModifierMappings = std::move(OldMappings);
714 }
715
716 private:
717 DiagTextVisitor &Visitor;
718 std::optional<std::vector<int>> OldMappings;
719
720 public:
721 Piece *Substitution;
722 };
723
724 public:
DiagTextVisitor__anonded1697c0511::DiagTextVisitor725 DiagTextVisitor(DiagnosticTextBuilder &Builder) : Builder(Builder) {}
726
Visit__anonded1697c0511::DiagTextVisitor727 void Visit(Piece *P) {
728 switch (P->getPieceClass()) {
729 #define CASE(T) \
730 case T##PieceClass: \
731 return getDerived().Visit##T(static_cast<T##Piece *>(P))
732 CASE(Multi);
733 CASE(Text);
734 CASE(Placeholder);
735 CASE(Select);
736 CASE(Plural);
737 CASE(Diff);
738 CASE(Substitution);
739 #undef CASE
740 }
741 }
742
VisitSubstitution__anonded1697c0511::DiagTextVisitor743 void VisitSubstitution(SubstitutionPiece *P) {
744 SubstitutionContext Guard(*this, P);
745 Visit(Guard.Substitution);
746 }
747
mapIndex__anonded1697c0511::DiagTextVisitor748 int mapIndex(int Idx,
749 ModifierMappingsType const &ModifierMappings) const {
750 if (!ModifierMappings)
751 return Idx;
752 if (ModifierMappings->size() <= static_cast<unsigned>(Idx))
753 Builder.PrintFatalError("Modifier value '" + std::to_string(Idx) +
754 "' is not valid for this mapping (has " +
755 std::to_string(ModifierMappings->size()) +
756 " mappings)");
757 return (*ModifierMappings)[Idx];
758 }
759
mapIndex__anonded1697c0511::DiagTextVisitor760 int mapIndex(int Idx) const {
761 return mapIndex(Idx, ModifierMappings);
762 }
763
764 protected:
765 DiagnosticTextBuilder &Builder;
766 ModifierMappingsType ModifierMappings;
767 };
768
escapeRST(StringRef Str,std::string & Out)769 void escapeRST(StringRef Str, std::string &Out) {
770 for (auto K : Str) {
771 if (StringRef("`*|_[]\\").count(K))
772 Out.push_back('\\');
773 Out.push_back(K);
774 }
775 }
776
padToSameLength(It Begin,It End)777 template <typename It> void padToSameLength(It Begin, It End) {
778 size_t Width = 0;
779 for (It I = Begin; I != End; ++I)
780 Width = std::max(Width, I->size());
781 for (It I = Begin; I != End; ++I)
782 (*I) += std::string(Width - I->size(), ' ');
783 }
784
makeTableRows(It Begin,It End)785 template <typename It> void makeTableRows(It Begin, It End) {
786 if (Begin == End)
787 return;
788 padToSameLength(Begin, End);
789 for (It I = Begin; I != End; ++I)
790 *I = "|" + *I + "|";
791 }
792
makeRowSeparator(std::string & Str)793 void makeRowSeparator(std::string &Str) {
794 for (char &K : Str)
795 K = (K == '|' ? '+' : '-');
796 }
797
798 struct DiagTextDocPrinter : DiagTextVisitor<DiagTextDocPrinter> {
799 using BaseTy = DiagTextVisitor<DiagTextDocPrinter>;
DiagTextDocPrinter__anonded1697c0511::DiagTextDocPrinter800 DiagTextDocPrinter(DiagnosticTextBuilder &Builder,
801 std::vector<std::string> &RST)
802 : BaseTy(Builder), RST(RST) {}
803
gatherNodes__anonded1697c0511::DiagTextDocPrinter804 void gatherNodes(
805 Piece *OrigP, const ModifierMappingsType &CurrentMappings,
806 std::vector<std::pair<Piece *, ModifierMappingsType>> &Pieces) const {
807 if (auto *Sub = dyn_cast<SubstitutionPiece>(OrigP)) {
808 ModifierMappingsType NewMappings =
809 getSubstitutionMappings(Sub, CurrentMappings);
810 return gatherNodes(Builder.getSubstitution(Sub), NewMappings, Pieces);
811 }
812 if (auto *MD = dyn_cast<MultiPiece>(OrigP)) {
813 for (Piece *Node : MD->Pieces)
814 gatherNodes(Node, CurrentMappings, Pieces);
815 return;
816 }
817 Pieces.push_back(std::make_pair(OrigP, CurrentMappings));
818 }
819
VisitMulti__anonded1697c0511::DiagTextDocPrinter820 void VisitMulti(MultiPiece *P) {
821 if (P->Pieces.empty()) {
822 RST.push_back("");
823 return;
824 }
825
826 if (P->Pieces.size() == 1)
827 return Visit(P->Pieces[0]);
828
829 // Flatten the list of nodes, replacing any substitution pieces with the
830 // recursively flattened substituted node.
831 std::vector<std::pair<Piece *, ModifierMappingsType>> Pieces;
832 gatherNodes(P, ModifierMappings, Pieces);
833
834 std::string EmptyLinePrefix;
835 size_t Start = RST.size();
836 bool HasMultipleLines = true;
837 for (const std::pair<Piece *, ModifierMappingsType> &NodePair : Pieces) {
838 std::vector<std::string> Lines;
839 DiagTextDocPrinter Visitor{Builder, Lines};
840 Visitor.ModifierMappings = NodePair.second;
841 Visitor.Visit(NodePair.first);
842
843 if (Lines.empty())
844 continue;
845
846 // We need a vertical separator if either this or the previous piece is a
847 // multi-line piece, or this is the last piece.
848 const char *Separator = (Lines.size() > 1 || HasMultipleLines) ? "|" : "";
849 HasMultipleLines = Lines.size() > 1;
850
851 if (Start + Lines.size() > RST.size())
852 RST.resize(Start + Lines.size(), EmptyLinePrefix);
853
854 padToSameLength(Lines.begin(), Lines.end());
855 for (size_t I = 0; I != Lines.size(); ++I)
856 RST[Start + I] += Separator + Lines[I];
857 std::string Empty(Lines[0].size(), ' ');
858 for (size_t I = Start + Lines.size(); I != RST.size(); ++I)
859 RST[I] += Separator + Empty;
860 EmptyLinePrefix += Separator + Empty;
861 }
862 for (size_t I = Start; I != RST.size(); ++I)
863 RST[I] += "|";
864 EmptyLinePrefix += "|";
865
866 makeRowSeparator(EmptyLinePrefix);
867 RST.insert(RST.begin() + Start, EmptyLinePrefix);
868 RST.insert(RST.end(), EmptyLinePrefix);
869 }
870
VisitText__anonded1697c0511::DiagTextDocPrinter871 void VisitText(TextPiece *P) {
872 RST.push_back("");
873 auto &S = RST.back();
874
875 StringRef T = P->Text;
876 while (T.consume_front(" "))
877 RST.back() += " |nbsp| ";
878
879 std::string Suffix;
880 while (T.consume_back(" "))
881 Suffix += " |nbsp| ";
882
883 if (!T.empty()) {
884 S += ':';
885 S += P->Role;
886 S += ":`";
887 escapeRST(T, S);
888 S += '`';
889 }
890
891 S += Suffix;
892 }
893
VisitPlaceholder__anonded1697c0511::DiagTextDocPrinter894 void VisitPlaceholder(PlaceholderPiece *P) {
895 RST.push_back(std::string(":placeholder:`") +
896 char('A' + mapIndex(P->Index)) + "`");
897 }
898
VisitSelect__anonded1697c0511::DiagTextDocPrinter899 void VisitSelect(SelectPiece *P) {
900 std::vector<size_t> SeparatorIndexes;
901 SeparatorIndexes.push_back(RST.size());
902 RST.emplace_back();
903 for (auto *O : P->Options) {
904 Visit(O);
905 SeparatorIndexes.push_back(RST.size());
906 RST.emplace_back();
907 }
908
909 makeTableRows(RST.begin() + SeparatorIndexes.front(),
910 RST.begin() + SeparatorIndexes.back() + 1);
911 for (size_t I : SeparatorIndexes)
912 makeRowSeparator(RST[I]);
913 }
914
VisitPlural__anonded1697c0511::DiagTextDocPrinter915 void VisitPlural(PluralPiece *P) { VisitSelect(P); }
916
VisitDiff__anonded1697c0511::DiagTextDocPrinter917 void VisitDiff(DiffPiece *P) {
918 // Render %diff{a $ b $ c|d}e,f as %select{a %e b %f c|d}.
919 PlaceholderPiece E(MT_Placeholder, P->Indexes[0]);
920 PlaceholderPiece F(MT_Placeholder, P->Indexes[1]);
921
922 MultiPiece FirstOption;
923 FirstOption.Pieces.push_back(P->Parts[0]);
924 FirstOption.Pieces.push_back(&E);
925 FirstOption.Pieces.push_back(P->Parts[1]);
926 FirstOption.Pieces.push_back(&F);
927 FirstOption.Pieces.push_back(P->Parts[2]);
928
929 SelectPiece Select(MT_Diff);
930 Select.Options.push_back(&FirstOption);
931 Select.Options.push_back(P->Parts[3]);
932
933 VisitSelect(&Select);
934 }
935
936 std::vector<std::string> &RST;
937 };
938
939 struct DiagTextPrinter : DiagTextVisitor<DiagTextPrinter> {
940 public:
941 using BaseTy = DiagTextVisitor<DiagTextPrinter>;
DiagTextPrinter__anonded1697c0511::DiagTextPrinter942 DiagTextPrinter(DiagnosticTextBuilder &Builder, std::string &Result)
943 : BaseTy(Builder), Result(Result) {}
944
VisitMulti__anonded1697c0511::DiagTextPrinter945 void VisitMulti(MultiPiece *P) {
946 for (auto *Child : P->Pieces)
947 Visit(Child);
948 }
VisitText__anonded1697c0511::DiagTextPrinter949 void VisitText(TextPiece *P) { Result += P->Text; }
VisitPlaceholder__anonded1697c0511::DiagTextPrinter950 void VisitPlaceholder(PlaceholderPiece *P) {
951 Result += "%";
952 Result += getModifierName(P->Kind);
953 addInt(mapIndex(P->Index));
954 }
VisitSelect__anonded1697c0511::DiagTextPrinter955 void VisitSelect(SelectPiece *P) {
956 Result += "%";
957 Result += getModifierName(P->ModKind);
958 if (P->ModKind == MT_Select) {
959 Result += "{";
960 for (auto *D : P->Options) {
961 Visit(D);
962 Result += '|';
963 }
964 if (!P->Options.empty())
965 Result.erase(--Result.end());
966 Result += '}';
967 }
968 addInt(mapIndex(P->Index));
969 }
970
VisitPlural__anonded1697c0511::DiagTextPrinter971 void VisitPlural(PluralPiece *P) {
972 Result += "%plural{";
973 assert(P->Options.size() == P->OptionPrefixes.size());
974 for (unsigned I = 0, End = P->Options.size(); I < End; ++I) {
975 if (P->OptionPrefixes[I])
976 Visit(P->OptionPrefixes[I]);
977 Visit(P->Options[I]);
978 Result += "|";
979 }
980 if (!P->Options.empty())
981 Result.erase(--Result.end());
982 Result += '}';
983 addInt(mapIndex(P->Index));
984 }
985
VisitDiff__anonded1697c0511::DiagTextPrinter986 void VisitDiff(DiffPiece *P) {
987 Result += "%diff{";
988 Visit(P->Parts[0]);
989 Result += "$";
990 Visit(P->Parts[1]);
991 Result += "$";
992 Visit(P->Parts[2]);
993 Result += "|";
994 Visit(P->Parts[3]);
995 Result += "}";
996 addInt(mapIndex(P->Indexes[0]));
997 Result += ",";
998 addInt(mapIndex(P->Indexes[1]));
999 }
1000
addInt__anonded1697c0511::DiagTextPrinter1001 void addInt(int Val) { Result += std::to_string(Val); }
1002
1003 std::string &Result;
1004 };
1005
parseModifier(StringRef & Text) const1006 int DiagnosticTextBuilder::DiagText::parseModifier(StringRef &Text) const {
1007 if (Text.empty() || !isdigit(Text[0]))
1008 Builder.PrintFatalError("expected modifier in diagnostic");
1009 int Val = 0;
1010 do {
1011 Val *= 10;
1012 Val += Text[0] - '0';
1013 Text = Text.drop_front();
1014 } while (!Text.empty() && isdigit(Text[0]));
1015 return Val;
1016 }
1017
parseDiagText(StringRef & Text,StopAt Stop)1018 Piece *DiagnosticTextBuilder::DiagText::parseDiagText(StringRef &Text,
1019 StopAt Stop) {
1020 std::vector<Piece *> Parsed;
1021
1022 constexpr llvm::StringLiteral StopSets[] = {"%", "%|}", "%|}$"};
1023 llvm::StringRef StopSet = StopSets[static_cast<int>(Stop)];
1024
1025 while (!Text.empty()) {
1026 size_t End = (size_t)-2;
1027 do
1028 End = Text.find_first_of(StopSet, End + 2);
1029 while (
1030 End < Text.size() - 1 && Text[End] == '%' &&
1031 (Text[End + 1] == '%' || Text[End + 1] == '|' || Text[End + 1] == '$'));
1032
1033 if (End) {
1034 Parsed.push_back(New<TextPiece>(Text.slice(0, End), "diagtext"));
1035 Text = Text.slice(End, StringRef::npos);
1036 if (Text.empty())
1037 break;
1038 }
1039
1040 if (Text[0] == '|' || Text[0] == '}' || Text[0] == '$')
1041 break;
1042
1043 // Drop the '%'.
1044 Text = Text.drop_front();
1045
1046 // Extract the (optional) modifier.
1047 size_t ModLength = Text.find_first_of("0123456789{");
1048 StringRef Modifier = Text.slice(0, ModLength);
1049 Text = Text.slice(ModLength, StringRef::npos);
1050 ModifierType ModType = llvm::StringSwitch<ModifierType>{Modifier}
1051 .Case("select", MT_Select)
1052 .Case("sub", MT_Sub)
1053 .Case("diff", MT_Diff)
1054 .Case("plural", MT_Plural)
1055 .Case("s", MT_S)
1056 .Case("ordinal", MT_Ordinal)
1057 .Case("q", MT_Q)
1058 .Case("objcclass", MT_ObjCClass)
1059 .Case("objcinstance", MT_ObjCInstance)
1060 .Case("", MT_Placeholder)
1061 .Default(MT_Unknown);
1062
1063 auto ExpectAndConsume = [&](StringRef Prefix) {
1064 if (!Text.consume_front(Prefix))
1065 Builder.PrintFatalError("expected '" + Prefix + "' while parsing %" +
1066 Modifier);
1067 };
1068
1069 switch (ModType) {
1070 case MT_Unknown:
1071 Builder.PrintFatalError("Unknown modifier type: " + Modifier);
1072 case MT_Select: {
1073 SelectPiece *Select = New<SelectPiece>(MT_Select);
1074 do {
1075 Text = Text.drop_front(); // '{' or '|'
1076 Select->Options.push_back(
1077 parseDiagText(Text, StopAt::PipeOrCloseBrace));
1078 assert(!Text.empty() && "malformed %select");
1079 } while (Text.front() == '|');
1080 ExpectAndConsume("}");
1081 Select->Index = parseModifier(Text);
1082 Parsed.push_back(Select);
1083 continue;
1084 }
1085 case MT_Plural: {
1086 PluralPiece *Plural = New<PluralPiece>();
1087 do {
1088 Text = Text.drop_front(); // '{' or '|'
1089 size_t End = Text.find_first_of(':');
1090 if (End == StringRef::npos)
1091 Builder.PrintFatalError("expected ':' while parsing %plural");
1092 ++End;
1093 assert(!Text.empty());
1094 Plural->OptionPrefixes.push_back(
1095 New<TextPiece>(Text.slice(0, End), "diagtext"));
1096 Text = Text.slice(End, StringRef::npos);
1097 Plural->Options.push_back(
1098 parseDiagText(Text, StopAt::PipeOrCloseBrace));
1099 assert(!Text.empty() && "malformed %plural");
1100 } while (Text.front() == '|');
1101 ExpectAndConsume("}");
1102 Plural->Index = parseModifier(Text);
1103 Parsed.push_back(Plural);
1104 continue;
1105 }
1106 case MT_Sub: {
1107 SubstitutionPiece *Sub = New<SubstitutionPiece>();
1108 ExpectAndConsume("{");
1109 size_t NameSize = Text.find_first_of('}');
1110 assert(NameSize != size_t(-1) && "failed to find the end of the name");
1111 assert(NameSize != 0 && "empty name?");
1112 Sub->Name = Text.substr(0, NameSize).str();
1113 Text = Text.drop_front(NameSize);
1114 ExpectAndConsume("}");
1115 if (!Text.empty()) {
1116 while (true) {
1117 if (!isdigit(Text[0]))
1118 break;
1119 Sub->Modifiers.push_back(parseModifier(Text));
1120 if (!Text.consume_front(","))
1121 break;
1122 assert(!Text.empty() && isdigit(Text[0]) &&
1123 "expected another modifier");
1124 }
1125 }
1126 Parsed.push_back(Sub);
1127 continue;
1128 }
1129 case MT_Diff: {
1130 DiffPiece *Diff = New<DiffPiece>();
1131 ExpectAndConsume("{");
1132 Diff->Parts[0] = parseDiagText(Text, StopAt::Dollar);
1133 ExpectAndConsume("$");
1134 Diff->Parts[1] = parseDiagText(Text, StopAt::Dollar);
1135 ExpectAndConsume("$");
1136 Diff->Parts[2] = parseDiagText(Text, StopAt::PipeOrCloseBrace);
1137 ExpectAndConsume("|");
1138 Diff->Parts[3] = parseDiagText(Text, StopAt::PipeOrCloseBrace);
1139 ExpectAndConsume("}");
1140 Diff->Indexes[0] = parseModifier(Text);
1141 ExpectAndConsume(",");
1142 Diff->Indexes[1] = parseModifier(Text);
1143 Parsed.push_back(Diff);
1144 continue;
1145 }
1146 case MT_S: {
1147 SelectPiece *Select = New<SelectPiece>(ModType);
1148 Select->Options.push_back(New<TextPiece>(""));
1149 Select->Options.push_back(New<TextPiece>("s", "diagtext"));
1150 Select->Index = parseModifier(Text);
1151 Parsed.push_back(Select);
1152 continue;
1153 }
1154 case MT_Q:
1155 case MT_Placeholder:
1156 case MT_ObjCClass:
1157 case MT_ObjCInstance:
1158 case MT_Ordinal: {
1159 Parsed.push_back(New<PlaceholderPiece>(ModType, parseModifier(Text)));
1160 continue;
1161 }
1162 }
1163 }
1164
1165 return New<MultiPiece>(Parsed);
1166 }
1167
1168 std::vector<std::string>
buildForDocumentation(StringRef Severity,const Record * R)1169 DiagnosticTextBuilder::buildForDocumentation(StringRef Severity,
1170 const Record *R) {
1171 EvaluatingRecordGuard Guard(&EvaluatingRecord, R);
1172 StringRef Text = R->getValueAsString("Summary");
1173
1174 DiagText D(*this, Text);
1175 TextPiece *Prefix = D.New<TextPiece>(Severity, Severity);
1176 Prefix->Text += ": ";
1177 auto *MP = dyn_cast<MultiPiece>(D.Root);
1178 if (!MP) {
1179 MP = D.New<MultiPiece>();
1180 MP->Pieces.push_back(D.Root);
1181 D.Root = MP;
1182 }
1183 MP->Pieces.insert(MP->Pieces.begin(), Prefix);
1184 std::vector<std::string> Result;
1185 DiagTextDocPrinter{*this, Result}.Visit(D.Root);
1186 return Result;
1187 }
1188
buildForDefinition(const Record * R)1189 std::string DiagnosticTextBuilder::buildForDefinition(const Record *R) {
1190 EvaluatingRecordGuard Guard(&EvaluatingRecord, R);
1191 StringRef Text = R->getValueAsString("Summary");
1192 DiagText D(*this, Text);
1193 std::string Result;
1194 DiagTextPrinter{*this, Result}.Visit(D.Root);
1195 return Result;
1196 }
1197
1198 } // namespace
1199
1200 //===----------------------------------------------------------------------===//
1201 // Warning Tables (.inc file) generation.
1202 //===----------------------------------------------------------------------===//
1203
isError(const Record & Diag)1204 static bool isError(const Record &Diag) {
1205 const std::string &ClsName =
1206 std::string(Diag.getValueAsDef("Class")->getName());
1207 return ClsName == "CLASS_ERROR";
1208 }
1209
isRemark(const Record & Diag)1210 static bool isRemark(const Record &Diag) {
1211 const std::string &ClsName =
1212 std::string(Diag.getValueAsDef("Class")->getName());
1213 return ClsName == "CLASS_REMARK";
1214 }
1215
1216 // Presumes the text has been split at the first whitespace or hyphen.
isExemptAtStart(StringRef Text)1217 static bool isExemptAtStart(StringRef Text) {
1218 // Fast path, the first character is lowercase or not alphanumeric.
1219 if (Text.empty() || isLower(Text[0]) || !isAlnum(Text[0]))
1220 return true;
1221
1222 // If the text is all uppercase (or numbers, +, or _), then we assume it's an
1223 // acronym and that's allowed. This covers cases like ISO, C23, C++14, and
1224 // OBJECT_MODE. However, if there's only a single letter other than "C", we
1225 // do not exempt it so that we catch a case like "A really bad idea" while
1226 // still allowing a case like "C does not allow...".
1227 if (llvm::all_of(Text, [](char C) {
1228 return isUpper(C) || isDigit(C) || C == '+' || C == '_';
1229 }))
1230 return Text.size() > 1 || Text[0] == 'C';
1231
1232 // Otherwise, there are a few other exemptions.
1233 return StringSwitch<bool>(Text)
1234 .Case("AddressSanitizer", true)
1235 .Case("CFString", true)
1236 .Case("Clang", true)
1237 .Case("Fuchsia", true)
1238 .Case("GNUstep", true)
1239 .Case("IBOutletCollection", true)
1240 .Case("Microsoft", true)
1241 .Case("Neon", true)
1242 .StartsWith("NSInvocation", true) // NSInvocation, NSInvocation's
1243 .Case("Objective", true) // Objective-C (hyphen is a word boundary)
1244 .Case("OpenACC", true)
1245 .Case("OpenCL", true)
1246 .Case("OpenMP", true)
1247 .Case("Pascal", true)
1248 .Case("Swift", true)
1249 .Case("Unicode", true)
1250 .Case("Vulkan", true)
1251 .Case("WebAssembly", true)
1252 .Default(false);
1253 }
1254
1255 // Does not presume the text has been split at all.
isExemptAtEnd(StringRef Text)1256 static bool isExemptAtEnd(StringRef Text) {
1257 // Rather than come up with a list of characters that are allowed, we go the
1258 // other way and look only for characters that are not allowed.
1259 switch (Text.back()) {
1260 default:
1261 return true;
1262 case '?':
1263 // Explicitly allowed to support "; did you mean?".
1264 return true;
1265 case '.':
1266 case '!':
1267 return false;
1268 }
1269 }
1270
verifyDiagnosticWording(const Record & Diag)1271 static void verifyDiagnosticWording(const Record &Diag) {
1272 StringRef FullDiagText = Diag.getValueAsString("Summary");
1273
1274 auto DiagnoseStart = [&](StringRef Text) {
1275 // Verify that the text does not start with a capital letter, except for
1276 // special cases that are exempt like ISO and C++. Find the first word
1277 // by looking for a word breaking character.
1278 char Separators[] = {' ', '-', ',', '}'};
1279 auto Iter = std::find_first_of(
1280 Text.begin(), Text.end(), std::begin(Separators), std::end(Separators));
1281
1282 StringRef First = Text.substr(0, Iter - Text.begin());
1283 if (!isExemptAtStart(First)) {
1284 PrintError(&Diag,
1285 "Diagnostics should not start with a capital letter; '" +
1286 First + "' is invalid");
1287 }
1288 };
1289
1290 auto DiagnoseEnd = [&](StringRef Text) {
1291 // Verify that the text does not end with punctuation like '.' or '!'.
1292 if (!isExemptAtEnd(Text)) {
1293 PrintError(&Diag, "Diagnostics should not end with punctuation; '" +
1294 Text.substr(Text.size() - 1, 1) + "' is invalid");
1295 }
1296 };
1297
1298 // If the diagnostic starts with %select, look through it to see whether any
1299 // of the options will cause a problem.
1300 if (FullDiagText.starts_with("%select{")) {
1301 // Do a balanced delimiter scan from the start of the text to find the
1302 // closing '}', skipping intermediary {} pairs.
1303
1304 size_t BraceCount = 1;
1305 constexpr size_t PercentSelectBraceLen = sizeof("%select{") - 1;
1306 auto Iter = FullDiagText.begin() + PercentSelectBraceLen;
1307 for (auto End = FullDiagText.end(); Iter != End; ++Iter) {
1308 char Ch = *Iter;
1309 if (Ch == '{')
1310 ++BraceCount;
1311 else if (Ch == '}')
1312 --BraceCount;
1313 if (!BraceCount)
1314 break;
1315 }
1316 // Defending against a malformed diagnostic string.
1317 if (BraceCount != 0)
1318 return;
1319
1320 StringRef SelectText =
1321 FullDiagText.substr(PercentSelectBraceLen, Iter - FullDiagText.begin() -
1322 PercentSelectBraceLen);
1323 SmallVector<StringRef, 4> SelectPieces;
1324 SelectText.split(SelectPieces, '|');
1325
1326 // Walk over all of the individual pieces of select text to see if any of
1327 // them start with an invalid character. If any of the select pieces is
1328 // empty, we need to look at the first word after the %select to see
1329 // whether that is invalid or not. If all of the pieces are fine, then we
1330 // don't need to check anything else about the start of the diagnostic.
1331 bool CheckSecondWord = false;
1332 for (StringRef Piece : SelectPieces) {
1333 if (Piece.empty())
1334 CheckSecondWord = true;
1335 else
1336 DiagnoseStart(Piece);
1337 }
1338
1339 if (CheckSecondWord) {
1340 // There was an empty select piece, so we need to check the second
1341 // word. This catches situations like '%select{|fine}0 Not okay'. Add
1342 // two to account for the closing curly brace and the number after it.
1343 StringRef AfterSelect =
1344 FullDiagText.substr(Iter - FullDiagText.begin() + 2).ltrim();
1345 DiagnoseStart(AfterSelect);
1346 }
1347 } else {
1348 // If the start of the diagnostic is not %select, we can check the first
1349 // word and be done with it.
1350 DiagnoseStart(FullDiagText);
1351 }
1352
1353 // If the last character in the diagnostic is a number preceded by a }, scan
1354 // backwards to see if this is for a %select{...}0. If it is, we need to look
1355 // at each piece to see whether it ends in punctuation or not.
1356 bool StillNeedToDiagEnd = true;
1357 if (isDigit(FullDiagText.back()) && *(FullDiagText.end() - 2) == '}') {
1358 // Scan backwards to find the opening curly brace.
1359 size_t BraceCount = 1;
1360 auto Iter = FullDiagText.end() - sizeof("}0");
1361 for (auto End = FullDiagText.begin(); Iter != End; --Iter) {
1362 char Ch = *Iter;
1363 if (Ch == '}')
1364 ++BraceCount;
1365 else if (Ch == '{')
1366 --BraceCount;
1367 if (!BraceCount)
1368 break;
1369 }
1370 // Defending against a malformed diagnostic string.
1371 if (BraceCount != 0)
1372 return;
1373
1374 // Continue the backwards scan to find the word before the '{' to see if it
1375 // is 'select'.
1376 constexpr size_t SelectLen = sizeof("select") - 1;
1377 bool IsSelect =
1378 (FullDiagText.substr(Iter - SelectLen - FullDiagText.begin(),
1379 SelectLen) == "select");
1380 if (IsSelect) {
1381 // Gather the content between the {} for the select in question so we can
1382 // split it into pieces.
1383 StillNeedToDiagEnd = false; // No longer need to handle the end.
1384 StringRef SelectText =
1385 FullDiagText.substr(Iter - FullDiagText.begin() + /*{*/ 1,
1386 FullDiagText.end() - Iter - /*pos before }0*/ 3);
1387 SmallVector<StringRef, 4> SelectPieces;
1388 SelectText.split(SelectPieces, '|');
1389 for (StringRef Piece : SelectPieces) {
1390 // Not worrying about a situation like: "this is bar. %select{foo|}0".
1391 if (!Piece.empty())
1392 DiagnoseEnd(Piece);
1393 }
1394 }
1395 }
1396
1397 // If we didn't already cover the diagnostic because of a %select, handle it
1398 // now.
1399 if (StillNeedToDiagEnd)
1400 DiagnoseEnd(FullDiagText);
1401
1402 // FIXME: This could also be improved by looking for instances of clang or
1403 // gcc in the diagnostic and recommend Clang or GCC instead. However, this
1404 // runs into odd situations like [[clang::warn_unused_result]],
1405 // #pragma clang, or --unwindlib=libgcc.
1406 }
1407
1408 /// ClangDiagsDefsEmitter - The top-level class emits .def files containing
1409 /// declarations of Clang diagnostics.
EmitClangDiagsDefs(RecordKeeper & Records,raw_ostream & OS,const std::string & Component)1410 void clang::EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS,
1411 const std::string &Component) {
1412 // Write the #if guard
1413 if (!Component.empty()) {
1414 std::string ComponentName = StringRef(Component).upper();
1415 OS << "#ifdef " << ComponentName << "START\n";
1416 OS << "__" << ComponentName << "START = DIAG_START_" << ComponentName
1417 << ",\n";
1418 OS << "#undef " << ComponentName << "START\n";
1419 OS << "#endif\n\n";
1420 }
1421
1422 DiagnosticTextBuilder DiagTextBuilder(Records);
1423
1424 std::vector<Record *> Diags = Records.getAllDerivedDefinitions("Diagnostic");
1425
1426 std::vector<Record*> DiagGroups
1427 = Records.getAllDerivedDefinitions("DiagGroup");
1428
1429 std::map<std::string, GroupInfo> DiagsInGroup;
1430 groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
1431
1432 DiagCategoryIDMap CategoryIDs(Records);
1433 DiagGroupParentMap DGParentMap(Records);
1434
1435 // Compute the set of diagnostics that are in -Wpedantic.
1436 RecordSet DiagsInPedantic;
1437 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
1438 inferPedantic.compute(&DiagsInPedantic, (RecordVec*)nullptr);
1439
1440 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
1441 const Record &R = *Diags[i];
1442
1443 // Check if this is an error that is accidentally in a warning
1444 // group.
1445 if (isError(R)) {
1446 if (DefInit *Group = dyn_cast<DefInit>(R.getValueInit("Group"))) {
1447 const Record *GroupRec = Group->getDef();
1448 const std::string &GroupName =
1449 std::string(GroupRec->getValueAsString("GroupName"));
1450 PrintFatalError(R.getLoc(), "Error " + R.getName() +
1451 " cannot be in a warning group [" + GroupName + "]");
1452 }
1453 }
1454
1455 // Check that all remarks have an associated diagnostic group.
1456 if (isRemark(R)) {
1457 if (!isa<DefInit>(R.getValueInit("Group"))) {
1458 PrintFatalError(R.getLoc(), "Error " + R.getName() +
1459 " not in any diagnostic group");
1460 }
1461 }
1462
1463 // Filter by component.
1464 if (!Component.empty() && Component != R.getValueAsString("Component"))
1465 continue;
1466
1467 // Validate diagnostic wording for common issues.
1468 verifyDiagnosticWording(R);
1469
1470 OS << "DIAG(" << R.getName() << ", ";
1471 OS << R.getValueAsDef("Class")->getName();
1472 OS << ", (unsigned)diag::Severity::"
1473 << R.getValueAsDef("DefaultSeverity")->getValueAsString("Name");
1474
1475 // Description string.
1476 OS << ", \"";
1477 OS.write_escaped(DiagTextBuilder.buildForDefinition(&R)) << '"';
1478
1479 // Warning group associated with the diagnostic. This is stored as an index
1480 // into the alphabetically sorted warning group table.
1481 if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) {
1482 std::map<std::string, GroupInfo>::iterator I = DiagsInGroup.find(
1483 std::string(DI->getDef()->getValueAsString("GroupName")));
1484 assert(I != DiagsInGroup.end());
1485 OS << ", " << I->second.IDNo;
1486 } else if (DiagsInPedantic.count(&R)) {
1487 std::map<std::string, GroupInfo>::iterator I =
1488 DiagsInGroup.find("pedantic");
1489 assert(I != DiagsInGroup.end() && "pedantic group not defined");
1490 OS << ", " << I->second.IDNo;
1491 } else {
1492 OS << ", 0";
1493 }
1494
1495 // SFINAE response.
1496 OS << ", " << R.getValueAsDef("SFINAE")->getName();
1497
1498 // Default warning has no Werror bit.
1499 if (R.getValueAsBit("WarningNoWerror"))
1500 OS << ", true";
1501 else
1502 OS << ", false";
1503
1504 if (R.getValueAsBit("ShowInSystemHeader"))
1505 OS << ", true";
1506 else
1507 OS << ", false";
1508
1509 if (R.getValueAsBit("ShowInSystemMacro"))
1510 OS << ", true";
1511 else
1512 OS << ", false";
1513
1514 if (R.getValueAsBit("Deferrable"))
1515 OS << ", true";
1516 else
1517 OS << ", false";
1518
1519 // Category number.
1520 OS << ", " << CategoryIDs.getID(getDiagnosticCategory(&R, DGParentMap));
1521 OS << ")\n";
1522 }
1523 }
1524
1525 //===----------------------------------------------------------------------===//
1526 // Warning Group Tables generation
1527 //===----------------------------------------------------------------------===//
1528
getDiagCategoryEnum(llvm::StringRef name)1529 static std::string getDiagCategoryEnum(llvm::StringRef name) {
1530 if (name.empty())
1531 return "DiagCat_None";
1532 SmallString<256> enumName = llvm::StringRef("DiagCat_");
1533 for (llvm::StringRef::iterator I = name.begin(), E = name.end(); I != E; ++I)
1534 enumName += isalnum(*I) ? *I : '_';
1535 return std::string(enumName);
1536 }
1537
1538 /// Emit the array of diagnostic subgroups.
1539 ///
1540 /// The array of diagnostic subgroups contains for each group a list of its
1541 /// subgroups. The individual lists are separated by '-1'. Groups with no
1542 /// subgroups are skipped.
1543 ///
1544 /// \code
1545 /// static const int16_t DiagSubGroups[] = {
1546 /// /* Empty */ -1,
1547 /// /* DiagSubGroup0 */ 142, -1,
1548 /// /* DiagSubGroup13 */ 265, 322, 399, -1
1549 /// }
1550 /// \endcode
1551 ///
emitDiagSubGroups(std::map<std::string,GroupInfo> & DiagsInGroup,RecordVec & GroupsInPedantic,raw_ostream & OS)1552 static void emitDiagSubGroups(std::map<std::string, GroupInfo> &DiagsInGroup,
1553 RecordVec &GroupsInPedantic, raw_ostream &OS) {
1554 OS << "static const int16_t DiagSubGroups[] = {\n"
1555 << " /* Empty */ -1,\n";
1556 for (auto const &I : DiagsInGroup) {
1557 const bool IsPedantic = I.first == "pedantic";
1558
1559 const std::vector<std::string> &SubGroups = I.second.SubGroups;
1560 if (!SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty())) {
1561 OS << " /* DiagSubGroup" << I.second.IDNo << " */ ";
1562 for (auto const &SubGroup : SubGroups) {
1563 std::map<std::string, GroupInfo>::const_iterator RI =
1564 DiagsInGroup.find(SubGroup);
1565 assert(RI != DiagsInGroup.end() && "Referenced without existing?");
1566 OS << RI->second.IDNo << ", ";
1567 }
1568 // Emit the groups implicitly in "pedantic".
1569 if (IsPedantic) {
1570 for (auto const &Group : GroupsInPedantic) {
1571 const std::string &GroupName =
1572 std::string(Group->getValueAsString("GroupName"));
1573 std::map<std::string, GroupInfo>::const_iterator RI =
1574 DiagsInGroup.find(GroupName);
1575 assert(RI != DiagsInGroup.end() && "Referenced without existing?");
1576 OS << RI->second.IDNo << ", ";
1577 }
1578 }
1579
1580 OS << "-1,\n";
1581 }
1582 }
1583 OS << "};\n\n";
1584 }
1585
1586 /// Emit the list of diagnostic arrays.
1587 ///
1588 /// This data structure is a large array that contains itself arrays of varying
1589 /// size. Each array represents a list of diagnostics. The different arrays are
1590 /// separated by the value '-1'.
1591 ///
1592 /// \code
1593 /// static const int16_t DiagArrays[] = {
1594 /// /* Empty */ -1,
1595 /// /* DiagArray1 */ diag::warn_pragma_message,
1596 /// -1,
1597 /// /* DiagArray2 */ diag::warn_abs_too_small,
1598 /// diag::warn_unsigned_abs,
1599 /// diag::warn_wrong_absolute_value_type,
1600 /// -1
1601 /// };
1602 /// \endcode
1603 ///
emitDiagArrays(std::map<std::string,GroupInfo> & DiagsInGroup,RecordVec & DiagsInPedantic,raw_ostream & OS)1604 static void emitDiagArrays(std::map<std::string, GroupInfo> &DiagsInGroup,
1605 RecordVec &DiagsInPedantic, raw_ostream &OS) {
1606 OS << "static const int16_t DiagArrays[] = {\n"
1607 << " /* Empty */ -1,\n";
1608 for (auto const &I : DiagsInGroup) {
1609 const bool IsPedantic = I.first == "pedantic";
1610
1611 const std::vector<const Record *> &V = I.second.DiagsInGroup;
1612 if (!V.empty() || (IsPedantic && !DiagsInPedantic.empty())) {
1613 OS << " /* DiagArray" << I.second.IDNo << " */ ";
1614 for (auto *Record : V)
1615 OS << "diag::" << Record->getName() << ", ";
1616 // Emit the diagnostics implicitly in "pedantic".
1617 if (IsPedantic) {
1618 for (auto const &Diag : DiagsInPedantic)
1619 OS << "diag::" << Diag->getName() << ", ";
1620 }
1621 OS << "-1,\n";
1622 }
1623 }
1624 OS << "};\n\n";
1625 }
1626
1627 /// Emit a list of group names.
1628 ///
1629 /// This creates a long string which by itself contains a list of pascal style
1630 /// strings, which consist of a length byte directly followed by the string.
1631 ///
1632 /// \code
1633 /// static const char DiagGroupNames[] = {
1634 /// \000\020#pragma-messages\t#warnings\020CFString-literal"
1635 /// };
1636 /// \endcode
emitDiagGroupNames(StringToOffsetTable & GroupNames,raw_ostream & OS)1637 static void emitDiagGroupNames(StringToOffsetTable &GroupNames,
1638 raw_ostream &OS) {
1639 OS << "static const char DiagGroupNames[] = {\n";
1640 GroupNames.EmitString(OS);
1641 OS << "};\n\n";
1642 }
1643
1644 /// Emit diagnostic arrays and related data structures.
1645 ///
1646 /// This creates the actual diagnostic array, an array of diagnostic subgroups
1647 /// and an array of subgroup names.
1648 ///
1649 /// \code
1650 /// #ifdef GET_DIAG_ARRAYS
1651 /// static const int16_t DiagArrays[];
1652 /// static const int16_t DiagSubGroups[];
1653 /// static const char DiagGroupNames[];
1654 /// #endif
1655 /// \endcode
emitAllDiagArrays(std::map<std::string,GroupInfo> & DiagsInGroup,RecordVec & DiagsInPedantic,RecordVec & GroupsInPedantic,StringToOffsetTable & GroupNames,raw_ostream & OS)1656 static void emitAllDiagArrays(std::map<std::string, GroupInfo> &DiagsInGroup,
1657 RecordVec &DiagsInPedantic,
1658 RecordVec &GroupsInPedantic,
1659 StringToOffsetTable &GroupNames,
1660 raw_ostream &OS) {
1661 OS << "\n#ifdef GET_DIAG_ARRAYS\n";
1662 emitDiagArrays(DiagsInGroup, DiagsInPedantic, OS);
1663 emitDiagSubGroups(DiagsInGroup, GroupsInPedantic, OS);
1664 emitDiagGroupNames(GroupNames, OS);
1665 OS << "#endif // GET_DIAG_ARRAYS\n\n";
1666 }
1667
1668 /// Emit diagnostic table.
1669 ///
1670 /// The table is sorted by the name of the diagnostic group. Each element
1671 /// consists of the name of the diagnostic group (given as offset in the
1672 /// group name table), a reference to a list of diagnostics (optional) and a
1673 /// reference to a set of subgroups (optional).
1674 ///
1675 /// \code
1676 /// #ifdef GET_DIAG_TABLE
1677 /// {/* abi */ 159, /* DiagArray11 */ 19, /* Empty */ 0},
1678 /// {/* aggregate-return */ 180, /* Empty */ 0, /* Empty */ 0},
1679 /// {/* all */ 197, /* Empty */ 0, /* DiagSubGroup13 */ 3},
1680 /// {/* deprecated */ 1981,/* DiagArray1 */ 348, /* DiagSubGroup3 */ 9},
1681 /// #endif
1682 /// \endcode
emitDiagTable(std::map<std::string,GroupInfo> & DiagsInGroup,RecordVec & DiagsInPedantic,RecordVec & GroupsInPedantic,StringToOffsetTable & GroupNames,raw_ostream & OS)1683 static void emitDiagTable(std::map<std::string, GroupInfo> &DiagsInGroup,
1684 RecordVec &DiagsInPedantic,
1685 RecordVec &GroupsInPedantic,
1686 StringToOffsetTable &GroupNames, raw_ostream &OS) {
1687 unsigned MaxLen = 0;
1688
1689 for (auto const &I: DiagsInGroup)
1690 MaxLen = std::max(MaxLen, (unsigned)I.first.size());
1691
1692 OS << "\n#ifdef DIAG_ENTRY\n";
1693 unsigned SubGroupIndex = 1, DiagArrayIndex = 1;
1694 for (auto const &I: DiagsInGroup) {
1695 // Group option string.
1696 OS << "DIAG_ENTRY(";
1697 OS << I.second.GroupName << " /* ";
1698
1699 if (I.first.find_first_not_of("abcdefghijklmnopqrstuvwxyz"
1700 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1701 "0123456789!@#$%^*-+=:?") !=
1702 std::string::npos)
1703 PrintFatalError("Invalid character in diagnostic group '" + I.first +
1704 "'");
1705 OS << I.first << " */, ";
1706 // Store a pascal-style length byte at the beginning of the string.
1707 std::string Name = char(I.first.size()) + I.first;
1708 OS << GroupNames.GetOrAddStringOffset(Name, false) << ", ";
1709
1710 // Special handling for 'pedantic'.
1711 const bool IsPedantic = I.first == "pedantic";
1712
1713 // Diagnostics in the group.
1714 const std::vector<const Record *> &V = I.second.DiagsInGroup;
1715 const bool hasDiags =
1716 !V.empty() || (IsPedantic && !DiagsInPedantic.empty());
1717 if (hasDiags) {
1718 OS << "/* DiagArray" << I.second.IDNo << " */ " << DiagArrayIndex
1719 << ", ";
1720 if (IsPedantic)
1721 DiagArrayIndex += DiagsInPedantic.size();
1722 DiagArrayIndex += V.size() + 1;
1723 } else {
1724 OS << "0, ";
1725 }
1726
1727 // Subgroups.
1728 const std::vector<std::string> &SubGroups = I.second.SubGroups;
1729 const bool hasSubGroups =
1730 !SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty());
1731 if (hasSubGroups) {
1732 OS << "/* DiagSubGroup" << I.second.IDNo << " */ " << SubGroupIndex
1733 << ", ";
1734 if (IsPedantic)
1735 SubGroupIndex += GroupsInPedantic.size();
1736 SubGroupIndex += SubGroups.size() + 1;
1737 } else {
1738 OS << "0, ";
1739 }
1740
1741 std::string Documentation = I.second.Defs.back()
1742 ->getValue("Documentation")
1743 ->getValue()
1744 ->getAsUnquotedString();
1745
1746 OS << "R\"(" << StringRef(Documentation).trim() << ")\"";
1747
1748 OS << ")\n";
1749 }
1750 OS << "#endif // DIAG_ENTRY\n\n";
1751 }
1752
1753 /// Emit the table of diagnostic categories.
1754 ///
1755 /// The table has the form of macro calls that have two parameters. The
1756 /// category's name as well as an enum that represents the category. The
1757 /// table can be used by defining the macro 'CATEGORY' and including this
1758 /// table right after.
1759 ///
1760 /// \code
1761 /// #ifdef GET_CATEGORY_TABLE
1762 /// CATEGORY("Semantic Issue", DiagCat_Semantic_Issue)
1763 /// CATEGORY("Lambda Issue", DiagCat_Lambda_Issue)
1764 /// #endif
1765 /// \endcode
emitCategoryTable(RecordKeeper & Records,raw_ostream & OS)1766 static void emitCategoryTable(RecordKeeper &Records, raw_ostream &OS) {
1767 DiagCategoryIDMap CategoriesByID(Records);
1768 OS << "\n#ifdef GET_CATEGORY_TABLE\n";
1769 for (auto const &C : CategoriesByID)
1770 OS << "CATEGORY(\"" << C << "\", " << getDiagCategoryEnum(C) << ")\n";
1771 OS << "#endif // GET_CATEGORY_TABLE\n\n";
1772 }
1773
EmitClangDiagGroups(RecordKeeper & Records,raw_ostream & OS)1774 void clang::EmitClangDiagGroups(RecordKeeper &Records, raw_ostream &OS) {
1775 // Compute a mapping from a DiagGroup to all of its parents.
1776 DiagGroupParentMap DGParentMap(Records);
1777
1778 std::vector<Record *> Diags = Records.getAllDerivedDefinitions("Diagnostic");
1779
1780 std::vector<Record *> DiagGroups =
1781 Records.getAllDerivedDefinitions("DiagGroup");
1782
1783 std::map<std::string, GroupInfo> DiagsInGroup;
1784 groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
1785
1786 // All extensions are implicitly in the "pedantic" group. Record the
1787 // implicit set of groups in the "pedantic" group, and use this information
1788 // later when emitting the group information for Pedantic.
1789 RecordVec DiagsInPedantic;
1790 RecordVec GroupsInPedantic;
1791 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
1792 inferPedantic.compute(&DiagsInPedantic, &GroupsInPedantic);
1793
1794 StringToOffsetTable GroupNames;
1795 for (std::map<std::string, GroupInfo>::const_iterator
1796 I = DiagsInGroup.begin(),
1797 E = DiagsInGroup.end();
1798 I != E; ++I) {
1799 // Store a pascal-style length byte at the beginning of the string.
1800 std::string Name = char(I->first.size()) + I->first;
1801 GroupNames.GetOrAddStringOffset(Name, false);
1802 }
1803
1804 emitAllDiagArrays(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames,
1805 OS);
1806 emitDiagTable(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames,
1807 OS);
1808 emitCategoryTable(Records, OS);
1809 }
1810
1811 //===----------------------------------------------------------------------===//
1812 // Diagnostic name index generation
1813 //===----------------------------------------------------------------------===//
1814
1815 namespace {
1816 struct RecordIndexElement
1817 {
RecordIndexElement__anonded1697c0a11::RecordIndexElement1818 RecordIndexElement() {}
RecordIndexElement__anonded1697c0a11::RecordIndexElement1819 explicit RecordIndexElement(Record const &R)
1820 : Name(std::string(R.getName())) {}
1821
1822 std::string Name;
1823 };
1824 } // end anonymous namespace.
1825
EmitClangDiagsIndexName(RecordKeeper & Records,raw_ostream & OS)1826 void clang::EmitClangDiagsIndexName(RecordKeeper &Records, raw_ostream &OS) {
1827 const std::vector<Record*> &Diags =
1828 Records.getAllDerivedDefinitions("Diagnostic");
1829
1830 std::vector<RecordIndexElement> Index;
1831 Index.reserve(Diags.size());
1832 for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
1833 const Record &R = *(Diags[i]);
1834 Index.push_back(RecordIndexElement(R));
1835 }
1836
1837 llvm::sort(Index,
1838 [](const RecordIndexElement &Lhs, const RecordIndexElement &Rhs) {
1839 return Lhs.Name < Rhs.Name;
1840 });
1841
1842 for (unsigned i = 0, e = Index.size(); i != e; ++i) {
1843 const RecordIndexElement &R = Index[i];
1844
1845 OS << "DIAG_NAME_INDEX(" << R.Name << ")\n";
1846 }
1847 }
1848
1849 //===----------------------------------------------------------------------===//
1850 // Diagnostic documentation generation
1851 //===----------------------------------------------------------------------===//
1852
1853 namespace docs {
1854 namespace {
1855
isRemarkGroup(const Record * DiagGroup,const std::map<std::string,GroupInfo> & DiagsInGroup)1856 bool isRemarkGroup(const Record *DiagGroup,
1857 const std::map<std::string, GroupInfo> &DiagsInGroup) {
1858 bool AnyRemarks = false, AnyNonRemarks = false;
1859
1860 std::function<void(StringRef)> Visit = [&](StringRef GroupName) {
1861 auto &GroupInfo = DiagsInGroup.find(std::string(GroupName))->second;
1862 for (const Record *Diag : GroupInfo.DiagsInGroup)
1863 (isRemark(*Diag) ? AnyRemarks : AnyNonRemarks) = true;
1864 for (const auto &Name : GroupInfo.SubGroups)
1865 Visit(Name);
1866 };
1867 Visit(DiagGroup->getValueAsString("GroupName"));
1868
1869 if (AnyRemarks && AnyNonRemarks)
1870 PrintFatalError(
1871 DiagGroup->getLoc(),
1872 "Diagnostic group contains both remark and non-remark diagnostics");
1873 return AnyRemarks;
1874 }
1875
getDefaultSeverity(const Record * Diag)1876 std::string getDefaultSeverity(const Record *Diag) {
1877 return std::string(
1878 Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name"));
1879 }
1880
1881 std::set<std::string>
getDefaultSeverities(const Record * DiagGroup,const std::map<std::string,GroupInfo> & DiagsInGroup)1882 getDefaultSeverities(const Record *DiagGroup,
1883 const std::map<std::string, GroupInfo> &DiagsInGroup) {
1884 std::set<std::string> States;
1885
1886 std::function<void(StringRef)> Visit = [&](StringRef GroupName) {
1887 auto &GroupInfo = DiagsInGroup.find(std::string(GroupName))->second;
1888 for (const Record *Diag : GroupInfo.DiagsInGroup)
1889 States.insert(getDefaultSeverity(Diag));
1890 for (const auto &Name : GroupInfo.SubGroups)
1891 Visit(Name);
1892 };
1893 Visit(DiagGroup->getValueAsString("GroupName"));
1894 return States;
1895 }
1896
writeHeader(StringRef Str,raw_ostream & OS,char Kind='-')1897 void writeHeader(StringRef Str, raw_ostream &OS, char Kind = '-') {
1898 OS << Str << "\n" << std::string(Str.size(), Kind) << "\n";
1899 }
1900
writeDiagnosticText(DiagnosticTextBuilder & Builder,const Record * R,StringRef Role,raw_ostream & OS)1901 void writeDiagnosticText(DiagnosticTextBuilder &Builder, const Record *R,
1902 StringRef Role, raw_ostream &OS) {
1903 StringRef Text = R->getValueAsString("Summary");
1904 if (Text == "%0")
1905 OS << "The text of this diagnostic is not controlled by Clang.\n\n";
1906 else {
1907 std::vector<std::string> Out = Builder.buildForDocumentation(Role, R);
1908 for (auto &Line : Out)
1909 OS << Line << "\n";
1910 OS << "\n";
1911 }
1912 }
1913
1914 } // namespace
1915 } // namespace docs
1916
EmitClangDiagDocs(RecordKeeper & Records,raw_ostream & OS)1917 void clang::EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS) {
1918 using namespace docs;
1919
1920 // Get the documentation introduction paragraph.
1921 const Record *Documentation = Records.getDef("GlobalDocumentation");
1922 if (!Documentation) {
1923 PrintFatalError("The Documentation top-level definition is missing, "
1924 "no documentation will be generated.");
1925 return;
1926 }
1927
1928 OS << Documentation->getValueAsString("Intro") << "\n";
1929
1930 DiagnosticTextBuilder Builder(Records);
1931
1932 std::vector<Record*> Diags =
1933 Records.getAllDerivedDefinitions("Diagnostic");
1934
1935 std::vector<Record*> DiagGroups =
1936 Records.getAllDerivedDefinitions("DiagGroup");
1937 llvm::sort(DiagGroups, diagGroupBeforeByName);
1938
1939 DiagGroupParentMap DGParentMap(Records);
1940
1941 std::map<std::string, GroupInfo> DiagsInGroup;
1942 groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
1943
1944 // Compute the set of diagnostics that are in -Wpedantic.
1945 {
1946 RecordSet DiagsInPedanticSet;
1947 RecordSet GroupsInPedanticSet;
1948 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
1949 inferPedantic.compute(&DiagsInPedanticSet, &GroupsInPedanticSet);
1950 auto &PedDiags = DiagsInGroup["pedantic"];
1951 // Put the diagnostics into a deterministic order.
1952 RecordVec DiagsInPedantic(DiagsInPedanticSet.begin(),
1953 DiagsInPedanticSet.end());
1954 RecordVec GroupsInPedantic(GroupsInPedanticSet.begin(),
1955 GroupsInPedanticSet.end());
1956 llvm::sort(DiagsInPedantic, beforeThanCompare);
1957 llvm::sort(GroupsInPedantic, beforeThanCompare);
1958 PedDiags.DiagsInGroup.insert(PedDiags.DiagsInGroup.end(),
1959 DiagsInPedantic.begin(),
1960 DiagsInPedantic.end());
1961 for (auto *Group : GroupsInPedantic)
1962 PedDiags.SubGroups.push_back(
1963 std::string(Group->getValueAsString("GroupName")));
1964 }
1965
1966 // FIXME: Write diagnostic categories and link to diagnostic groups in each.
1967
1968 // Write out the diagnostic groups.
1969 for (const Record *G : DiagGroups) {
1970 bool IsRemarkGroup = isRemarkGroup(G, DiagsInGroup);
1971 auto &GroupInfo =
1972 DiagsInGroup[std::string(G->getValueAsString("GroupName"))];
1973 bool IsSynonym = GroupInfo.DiagsInGroup.empty() &&
1974 GroupInfo.SubGroups.size() == 1;
1975
1976 writeHeader(((IsRemarkGroup ? "-R" : "-W") +
1977 G->getValueAsString("GroupName")).str(),
1978 OS);
1979
1980 if (!IsSynonym) {
1981 // FIXME: Ideally, all the diagnostics in a group should have the same
1982 // default state, but that is not currently the case.
1983 auto DefaultSeverities = getDefaultSeverities(G, DiagsInGroup);
1984 if (!DefaultSeverities.empty() && !DefaultSeverities.count("Ignored")) {
1985 bool AnyNonErrors = DefaultSeverities.count("Warning") ||
1986 DefaultSeverities.count("Remark");
1987 if (!AnyNonErrors)
1988 OS << "This diagnostic is an error by default, but the flag ``-Wno-"
1989 << G->getValueAsString("GroupName") << "`` can be used to disable "
1990 << "the error.\n\n";
1991 else
1992 OS << "This diagnostic is enabled by default.\n\n";
1993 } else if (DefaultSeverities.size() > 1) {
1994 OS << "Some of the diagnostics controlled by this flag are enabled "
1995 << "by default.\n\n";
1996 }
1997 }
1998
1999 if (!GroupInfo.SubGroups.empty()) {
2000 if (IsSynonym)
2001 OS << "Synonym for ";
2002 else if (GroupInfo.DiagsInGroup.empty())
2003 OS << "Controls ";
2004 else
2005 OS << "Also controls ";
2006
2007 bool First = true;
2008 llvm::sort(GroupInfo.SubGroups);
2009 for (const auto &Name : GroupInfo.SubGroups) {
2010 if (!First) OS << ", ";
2011 OS << "`" << (IsRemarkGroup ? "-R" : "-W") << Name << "`_";
2012 First = false;
2013 }
2014 OS << ".\n\n";
2015 }
2016
2017 if (!GroupInfo.DiagsInGroup.empty()) {
2018 OS << "**Diagnostic text:**\n\n";
2019 for (const Record *D : GroupInfo.DiagsInGroup) {
2020 auto Severity = getDefaultSeverity(D);
2021 Severity[0] = tolower(Severity[0]);
2022 if (Severity == "ignored")
2023 Severity = IsRemarkGroup ? "remark" : "warning";
2024
2025 writeDiagnosticText(Builder, D, Severity, OS);
2026 }
2027 }
2028
2029 auto Doc = G->getValueAsString("Documentation");
2030 if (!Doc.empty())
2031 OS << Doc;
2032 else if (GroupInfo.SubGroups.empty() && GroupInfo.DiagsInGroup.empty())
2033 OS << "This diagnostic flag exists for GCC compatibility, and has no "
2034 "effect in Clang.\n";
2035 OS << "\n";
2036 }
2037 }
2038