xref: /freebsd-src/contrib/llvm-project/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //=- ClangDiagnosticsEmitter.cpp - Generate Clang diagnostics tables -*- C++ -*-
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // These tablegen backends emit Clang diagnostics tables.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
13a7dea167SDimitry Andric #include "TableGenBackends.h"
140b57cec5SDimitry Andric #include "llvm/ADT/DenseSet.h"
150b57cec5SDimitry Andric #include "llvm/ADT/PointerUnion.h"
160b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
170b57cec5SDimitry Andric #include "llvm/ADT/SmallPtrSet.h"
180b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
190b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
200b57cec5SDimitry Andric #include "llvm/ADT/StringMap.h"
215ffd83dbSDimitry Andric #include "llvm/ADT/StringSwitch.h"
220b57cec5SDimitry Andric #include "llvm/ADT/Twine.h"
230b57cec5SDimitry Andric #include "llvm/Support/Casting.h"
240b57cec5SDimitry Andric #include "llvm/TableGen/Error.h"
250b57cec5SDimitry Andric #include "llvm/TableGen/Record.h"
260b57cec5SDimitry Andric #include "llvm/TableGen/StringToOffsetTable.h"
270b57cec5SDimitry Andric #include "llvm/TableGen/TableGenBackend.h"
280b57cec5SDimitry Andric #include <algorithm>
290b57cec5SDimitry Andric #include <cctype>
300b57cec5SDimitry Andric #include <functional>
310b57cec5SDimitry Andric #include <map>
32bdd1243dSDimitry Andric #include <optional>
330b57cec5SDimitry Andric #include <set>
340b57cec5SDimitry Andric using namespace llvm;
350b57cec5SDimitry Andric 
360b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
370b57cec5SDimitry Andric // Diagnostic category computation code.
380b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
390b57cec5SDimitry Andric 
400b57cec5SDimitry Andric namespace {
410b57cec5SDimitry Andric class DiagGroupParentMap {
420b57cec5SDimitry Andric   RecordKeeper &Records;
430b57cec5SDimitry Andric   std::map<const Record*, std::vector<Record*> > Mapping;
440b57cec5SDimitry Andric public:
450b57cec5SDimitry Andric   DiagGroupParentMap(RecordKeeper &records) : Records(records) {
460b57cec5SDimitry Andric     std::vector<Record*> DiagGroups
470b57cec5SDimitry Andric       = Records.getAllDerivedDefinitions("DiagGroup");
480b57cec5SDimitry Andric     for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) {
490b57cec5SDimitry Andric       std::vector<Record*> SubGroups =
500b57cec5SDimitry Andric         DiagGroups[i]->getValueAsListOfDefs("SubGroups");
510b57cec5SDimitry Andric       for (unsigned j = 0, e = SubGroups.size(); j != e; ++j)
520b57cec5SDimitry Andric         Mapping[SubGroups[j]].push_back(DiagGroups[i]);
530b57cec5SDimitry Andric     }
540b57cec5SDimitry Andric   }
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric   const std::vector<Record*> &getParents(const Record *Group) {
570b57cec5SDimitry Andric     return Mapping[Group];
580b57cec5SDimitry Andric   }
590b57cec5SDimitry Andric };
600b57cec5SDimitry Andric } // end anonymous namespace.
610b57cec5SDimitry Andric 
620b57cec5SDimitry Andric static std::string
630b57cec5SDimitry Andric getCategoryFromDiagGroup(const Record *Group,
640b57cec5SDimitry Andric                          DiagGroupParentMap &DiagGroupParents) {
650b57cec5SDimitry Andric   // If the DiagGroup has a category, return it.
665ffd83dbSDimitry Andric   std::string CatName = std::string(Group->getValueAsString("CategoryName"));
670b57cec5SDimitry Andric   if (!CatName.empty()) return CatName;
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric   // The diag group may the subgroup of one or more other diagnostic groups,
700b57cec5SDimitry Andric   // check these for a category as well.
710b57cec5SDimitry Andric   const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
720b57cec5SDimitry Andric   for (unsigned i = 0, e = Parents.size(); i != e; ++i) {
730b57cec5SDimitry Andric     CatName = getCategoryFromDiagGroup(Parents[i], DiagGroupParents);
740b57cec5SDimitry Andric     if (!CatName.empty()) return CatName;
750b57cec5SDimitry Andric   }
760b57cec5SDimitry Andric   return "";
770b57cec5SDimitry Andric }
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric /// getDiagnosticCategory - Return the category that the specified diagnostic
800b57cec5SDimitry Andric /// lives in.
810b57cec5SDimitry Andric static std::string getDiagnosticCategory(const Record *R,
820b57cec5SDimitry Andric                                          DiagGroupParentMap &DiagGroupParents) {
830b57cec5SDimitry Andric   // If the diagnostic is in a group, and that group has a category, use it.
840b57cec5SDimitry Andric   if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) {
850b57cec5SDimitry Andric     // Check the diagnostic's diag group for a category.
860b57cec5SDimitry Andric     std::string CatName = getCategoryFromDiagGroup(Group->getDef(),
870b57cec5SDimitry Andric                                                    DiagGroupParents);
880b57cec5SDimitry Andric     if (!CatName.empty()) return CatName;
890b57cec5SDimitry Andric   }
900b57cec5SDimitry Andric 
910b57cec5SDimitry Andric   // If the diagnostic itself has a category, get it.
925ffd83dbSDimitry Andric   return std::string(R->getValueAsString("CategoryName"));
930b57cec5SDimitry Andric }
940b57cec5SDimitry Andric 
950b57cec5SDimitry Andric namespace {
960b57cec5SDimitry Andric   class DiagCategoryIDMap {
970b57cec5SDimitry Andric     RecordKeeper &Records;
980b57cec5SDimitry Andric     StringMap<unsigned> CategoryIDs;
990b57cec5SDimitry Andric     std::vector<std::string> CategoryStrings;
1000b57cec5SDimitry Andric   public:
1010b57cec5SDimitry Andric     DiagCategoryIDMap(RecordKeeper &records) : Records(records) {
1020b57cec5SDimitry Andric       DiagGroupParentMap ParentInfo(Records);
1030b57cec5SDimitry Andric 
1040b57cec5SDimitry Andric       // The zero'th category is "".
1050b57cec5SDimitry Andric       CategoryStrings.push_back("");
1060b57cec5SDimitry Andric       CategoryIDs[""] = 0;
1070b57cec5SDimitry Andric 
1080b57cec5SDimitry Andric       std::vector<Record*> Diags =
1090b57cec5SDimitry Andric       Records.getAllDerivedDefinitions("Diagnostic");
1100b57cec5SDimitry Andric       for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
1110b57cec5SDimitry Andric         std::string Category = getDiagnosticCategory(Diags[i], ParentInfo);
1120b57cec5SDimitry Andric         if (Category.empty()) continue;  // Skip diags with no category.
1130b57cec5SDimitry Andric 
1140b57cec5SDimitry Andric         unsigned &ID = CategoryIDs[Category];
1150b57cec5SDimitry Andric         if (ID != 0) continue;  // Already seen.
1160b57cec5SDimitry Andric 
1170b57cec5SDimitry Andric         ID = CategoryStrings.size();
1180b57cec5SDimitry Andric         CategoryStrings.push_back(Category);
1190b57cec5SDimitry Andric       }
1200b57cec5SDimitry Andric     }
1210b57cec5SDimitry Andric 
1220b57cec5SDimitry Andric     unsigned getID(StringRef CategoryString) {
1230b57cec5SDimitry Andric       return CategoryIDs[CategoryString];
1240b57cec5SDimitry Andric     }
1250b57cec5SDimitry Andric 
1260b57cec5SDimitry Andric     typedef std::vector<std::string>::const_iterator const_iterator;
1270b57cec5SDimitry Andric     const_iterator begin() const { return CategoryStrings.begin(); }
1280b57cec5SDimitry Andric     const_iterator end() const { return CategoryStrings.end(); }
1290b57cec5SDimitry Andric   };
1300b57cec5SDimitry Andric 
1310b57cec5SDimitry Andric   struct GroupInfo {
132349cc55cSDimitry Andric     llvm::StringRef GroupName;
1330b57cec5SDimitry Andric     std::vector<const Record*> DiagsInGroup;
1340b57cec5SDimitry Andric     std::vector<std::string> SubGroups;
1355f757f3fSDimitry Andric     unsigned IDNo = 0;
1360b57cec5SDimitry Andric 
137fe6060f1SDimitry Andric     llvm::SmallVector<const Record *, 1> Defs;
1380b57cec5SDimitry Andric 
1395f757f3fSDimitry Andric     GroupInfo() = default;
1400b57cec5SDimitry Andric   };
1410b57cec5SDimitry Andric } // end anonymous namespace.
1420b57cec5SDimitry Andric 
1430b57cec5SDimitry Andric static bool beforeThanCompare(const Record *LHS, const Record *RHS) {
1440b57cec5SDimitry Andric   assert(!LHS->getLoc().empty() && !RHS->getLoc().empty());
1450b57cec5SDimitry Andric   return
1460b57cec5SDimitry Andric     LHS->getLoc().front().getPointer() < RHS->getLoc().front().getPointer();
1470b57cec5SDimitry Andric }
1480b57cec5SDimitry Andric 
1490b57cec5SDimitry Andric static bool diagGroupBeforeByName(const Record *LHS, const Record *RHS) {
1500b57cec5SDimitry Andric   return LHS->getValueAsString("GroupName") <
1510b57cec5SDimitry Andric          RHS->getValueAsString("GroupName");
1520b57cec5SDimitry Andric }
1530b57cec5SDimitry Andric 
1540b57cec5SDimitry Andric /// Invert the 1-[0/1] mapping of diags to group into a one to many
1550b57cec5SDimitry Andric /// mapping of groups to diags in the group.
1560b57cec5SDimitry Andric static void groupDiagnostics(const std::vector<Record*> &Diags,
1570b57cec5SDimitry Andric                              const std::vector<Record*> &DiagGroups,
1580b57cec5SDimitry Andric                              std::map<std::string, GroupInfo> &DiagsInGroup) {
1590b57cec5SDimitry Andric 
1600b57cec5SDimitry Andric   for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
1610b57cec5SDimitry Andric     const Record *R = Diags[i];
1620b57cec5SDimitry Andric     DefInit *DI = dyn_cast<DefInit>(R->getValueInit("Group"));
1630b57cec5SDimitry Andric     if (!DI)
1640b57cec5SDimitry Andric       continue;
1650b57cec5SDimitry Andric     assert(R->getValueAsDef("Class")->getName() != "CLASS_NOTE" &&
1660b57cec5SDimitry Andric            "Note can't be in a DiagGroup");
1675ffd83dbSDimitry Andric     std::string GroupName =
1685ffd83dbSDimitry Andric         std::string(DI->getDef()->getValueAsString("GroupName"));
1690b57cec5SDimitry Andric     DiagsInGroup[GroupName].DiagsInGroup.push_back(R);
1700b57cec5SDimitry Andric   }
1710b57cec5SDimitry Andric 
1720b57cec5SDimitry Andric   // Add all DiagGroup's to the DiagsInGroup list to make sure we pick up empty
1730b57cec5SDimitry Andric   // groups (these are warnings that GCC supports that clang never produces).
1740b57cec5SDimitry Andric   for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) {
1750b57cec5SDimitry Andric     Record *Group = DiagGroups[i];
1765ffd83dbSDimitry Andric     GroupInfo &GI =
1775ffd83dbSDimitry Andric         DiagsInGroup[std::string(Group->getValueAsString("GroupName"))];
178349cc55cSDimitry Andric     GI.GroupName = Group->getName();
179fe6060f1SDimitry Andric     GI.Defs.push_back(Group);
1800b57cec5SDimitry Andric 
1810b57cec5SDimitry Andric     std::vector<Record*> SubGroups = Group->getValueAsListOfDefs("SubGroups");
1820b57cec5SDimitry Andric     for (unsigned j = 0, e = SubGroups.size(); j != e; ++j)
1835ffd83dbSDimitry Andric       GI.SubGroups.push_back(
1845ffd83dbSDimitry Andric           std::string(SubGroups[j]->getValueAsString("GroupName")));
1850b57cec5SDimitry Andric   }
1860b57cec5SDimitry Andric 
1870b57cec5SDimitry Andric   // Assign unique ID numbers to the groups.
1880b57cec5SDimitry Andric   unsigned IDNo = 0;
1890b57cec5SDimitry Andric   for (std::map<std::string, GroupInfo>::iterator
1900b57cec5SDimitry Andric        I = DiagsInGroup.begin(), E = DiagsInGroup.end(); I != E; ++I, ++IDNo)
1910b57cec5SDimitry Andric     I->second.IDNo = IDNo;
1920b57cec5SDimitry Andric 
193fe6060f1SDimitry Andric   // Warn if the same group is defined more than once (including implicitly).
194fe6060f1SDimitry Andric   for (auto &Group : DiagsInGroup) {
195fe6060f1SDimitry Andric     if (Group.second.Defs.size() == 1 &&
196fe6060f1SDimitry Andric         (!Group.second.Defs.front()->isAnonymous() ||
197fe6060f1SDimitry Andric          Group.second.DiagsInGroup.size() <= 1))
1980b57cec5SDimitry Andric       continue;
1990b57cec5SDimitry Andric 
200fe6060f1SDimitry Andric     bool First = true;
201fe6060f1SDimitry Andric     for (const Record *Def : Group.second.Defs) {
202fe6060f1SDimitry Andric       // Skip implicit definitions from diagnostics; we'll report those
203fe6060f1SDimitry Andric       // separately below.
204fe6060f1SDimitry Andric       bool IsImplicit = false;
205fe6060f1SDimitry Andric       for (const Record *Diag : Group.second.DiagsInGroup) {
206fe6060f1SDimitry Andric         if (cast<DefInit>(Diag->getValueInit("Group"))->getDef() == Def) {
207fe6060f1SDimitry Andric           IsImplicit = true;
208fe6060f1SDimitry Andric           break;
2090b57cec5SDimitry Andric         }
210fe6060f1SDimitry Andric       }
211fe6060f1SDimitry Andric       if (IsImplicit)
212fe6060f1SDimitry Andric         continue;
213fe6060f1SDimitry Andric 
214fe6060f1SDimitry Andric       llvm::SMLoc Loc = Def->getLoc().front();
215fe6060f1SDimitry Andric       if (First) {
216fe6060f1SDimitry Andric         SrcMgr.PrintMessage(Loc, SourceMgr::DK_Error,
217fe6060f1SDimitry Andric                             Twine("group '") + Group.first +
218fe6060f1SDimitry Andric                                 "' is defined more than once");
219fe6060f1SDimitry Andric         First = false;
2200b57cec5SDimitry Andric       } else {
221fe6060f1SDimitry Andric         SrcMgr.PrintMessage(Loc, SourceMgr::DK_Note, "also defined here");
222fe6060f1SDimitry Andric       }
223fe6060f1SDimitry Andric     }
2240b57cec5SDimitry Andric 
225fe6060f1SDimitry Andric     for (const Record *Diag : Group.second.DiagsInGroup) {
226fe6060f1SDimitry Andric       if (!cast<DefInit>(Diag->getValueInit("Group"))->getDef()->isAnonymous())
227fe6060f1SDimitry Andric         continue;
2280b57cec5SDimitry Andric 
229fe6060f1SDimitry Andric       llvm::SMLoc Loc = Diag->getLoc().front();
230fe6060f1SDimitry Andric       if (First) {
231fe6060f1SDimitry Andric         SrcMgr.PrintMessage(Loc, SourceMgr::DK_Error,
232fe6060f1SDimitry Andric                             Twine("group '") + Group.first +
233fe6060f1SDimitry Andric                                 "' is implicitly defined more than once");
234fe6060f1SDimitry Andric         First = false;
235fe6060f1SDimitry Andric       } else {
236fe6060f1SDimitry Andric         SrcMgr.PrintMessage(Loc, SourceMgr::DK_Note,
237fe6060f1SDimitry Andric                             "also implicitly defined here");
2380b57cec5SDimitry Andric       }
2390b57cec5SDimitry Andric     }
2400b57cec5SDimitry Andric   }
2410b57cec5SDimitry Andric }
2420b57cec5SDimitry Andric 
2430b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
2440b57cec5SDimitry Andric // Infer members of -Wpedantic.
2450b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
2460b57cec5SDimitry Andric 
2470b57cec5SDimitry Andric typedef std::vector<const Record *> RecordVec;
2480b57cec5SDimitry Andric typedef llvm::DenseSet<const Record *> RecordSet;
2490b57cec5SDimitry Andric typedef llvm::PointerUnion<RecordVec*, RecordSet*> VecOrSet;
2500b57cec5SDimitry Andric 
2510b57cec5SDimitry Andric namespace {
2520b57cec5SDimitry Andric class InferPedantic {
2530b57cec5SDimitry Andric   typedef llvm::DenseMap<const Record *,
254bdd1243dSDimitry Andric                          std::pair<unsigned, std::optional<unsigned>>>
255bdd1243dSDimitry Andric       GMap;
2560b57cec5SDimitry Andric 
2570b57cec5SDimitry Andric   DiagGroupParentMap &DiagGroupParents;
2580b57cec5SDimitry Andric   const std::vector<Record*> &Diags;
2590b57cec5SDimitry Andric   const std::vector<Record*> DiagGroups;
2600b57cec5SDimitry Andric   std::map<std::string, GroupInfo> &DiagsInGroup;
2610b57cec5SDimitry Andric   llvm::DenseSet<const Record*> DiagsSet;
2620b57cec5SDimitry Andric   GMap GroupCount;
2630b57cec5SDimitry Andric public:
2640b57cec5SDimitry Andric   InferPedantic(DiagGroupParentMap &DiagGroupParents,
2650b57cec5SDimitry Andric                 const std::vector<Record*> &Diags,
2660b57cec5SDimitry Andric                 const std::vector<Record*> &DiagGroups,
2670b57cec5SDimitry Andric                 std::map<std::string, GroupInfo> &DiagsInGroup)
2680b57cec5SDimitry Andric   : DiagGroupParents(DiagGroupParents),
2690b57cec5SDimitry Andric   Diags(Diags),
2700b57cec5SDimitry Andric   DiagGroups(DiagGroups),
2710b57cec5SDimitry Andric   DiagsInGroup(DiagsInGroup) {}
2720b57cec5SDimitry Andric 
2730b57cec5SDimitry Andric   /// Compute the set of diagnostics and groups that are immediately
2740b57cec5SDimitry Andric   /// in -Wpedantic.
2750b57cec5SDimitry Andric   void compute(VecOrSet DiagsInPedantic,
2760b57cec5SDimitry Andric                VecOrSet GroupsInPedantic);
2770b57cec5SDimitry Andric 
2780b57cec5SDimitry Andric private:
2790b57cec5SDimitry Andric   /// Determine whether a group is a subgroup of another group.
2800b57cec5SDimitry Andric   bool isSubGroupOfGroup(const Record *Group,
2810b57cec5SDimitry Andric                          llvm::StringRef RootGroupName);
2820b57cec5SDimitry Andric 
2830b57cec5SDimitry Andric   /// Determine if the diagnostic is an extension.
2840b57cec5SDimitry Andric   bool isExtension(const Record *Diag);
2850b57cec5SDimitry Andric 
2860b57cec5SDimitry Andric   /// Determine if the diagnostic is off by default.
2870b57cec5SDimitry Andric   bool isOffByDefault(const Record *Diag);
2880b57cec5SDimitry Andric 
2890b57cec5SDimitry Andric   /// Increment the count for a group, and transitively marked
2900b57cec5SDimitry Andric   /// parent groups when appropriate.
2910b57cec5SDimitry Andric   void markGroup(const Record *Group);
2920b57cec5SDimitry Andric 
2930b57cec5SDimitry Andric   /// Return true if the diagnostic is in a pedantic group.
2940b57cec5SDimitry Andric   bool groupInPedantic(const Record *Group, bool increment = false);
2950b57cec5SDimitry Andric };
2960b57cec5SDimitry Andric } // end anonymous namespace
2970b57cec5SDimitry Andric 
2980b57cec5SDimitry Andric bool InferPedantic::isSubGroupOfGroup(const Record *Group,
2990b57cec5SDimitry Andric                                       llvm::StringRef GName) {
3005ffd83dbSDimitry Andric   const std::string &GroupName =
3015ffd83dbSDimitry Andric       std::string(Group->getValueAsString("GroupName"));
3020b57cec5SDimitry Andric   if (GName == GroupName)
3030b57cec5SDimitry Andric     return true;
3040b57cec5SDimitry Andric 
3050b57cec5SDimitry Andric   const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
3060b57cec5SDimitry Andric   for (unsigned i = 0, e = Parents.size(); i != e; ++i)
3070b57cec5SDimitry Andric     if (isSubGroupOfGroup(Parents[i], GName))
3080b57cec5SDimitry Andric       return true;
3090b57cec5SDimitry Andric 
3100b57cec5SDimitry Andric   return false;
3110b57cec5SDimitry Andric }
3120b57cec5SDimitry Andric 
3130b57cec5SDimitry Andric /// Determine if the diagnostic is an extension.
3140b57cec5SDimitry Andric bool InferPedantic::isExtension(const Record *Diag) {
3155ffd83dbSDimitry Andric   const std::string &ClsName =
3165ffd83dbSDimitry Andric       std::string(Diag->getValueAsDef("Class")->getName());
3170b57cec5SDimitry Andric   return ClsName == "CLASS_EXTENSION";
3180b57cec5SDimitry Andric }
3190b57cec5SDimitry Andric 
3200b57cec5SDimitry Andric bool InferPedantic::isOffByDefault(const Record *Diag) {
3215ffd83dbSDimitry Andric   const std::string &DefSeverity = std::string(
3225ffd83dbSDimitry Andric       Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name"));
3230b57cec5SDimitry Andric   return DefSeverity == "Ignored";
3240b57cec5SDimitry Andric }
3250b57cec5SDimitry Andric 
3260b57cec5SDimitry Andric bool InferPedantic::groupInPedantic(const Record *Group, bool increment) {
3270b57cec5SDimitry Andric   GMap::mapped_type &V = GroupCount[Group];
3280b57cec5SDimitry Andric   // Lazily compute the threshold value for the group count.
32981ad6265SDimitry Andric   if (!V.second) {
3305ffd83dbSDimitry Andric     const GroupInfo &GI =
3315ffd83dbSDimitry Andric         DiagsInGroup[std::string(Group->getValueAsString("GroupName"))];
3320b57cec5SDimitry Andric     V.second = GI.SubGroups.size() + GI.DiagsInGroup.size();
3330b57cec5SDimitry Andric   }
3340b57cec5SDimitry Andric 
3350b57cec5SDimitry Andric   if (increment)
3360b57cec5SDimitry Andric     ++V.first;
3370b57cec5SDimitry Andric 
3380b57cec5SDimitry Andric   // Consider a group in -Wpendatic IFF if has at least one diagnostic
3390b57cec5SDimitry Andric   // or subgroup AND all of those diagnostics and subgroups are covered
3400b57cec5SDimitry Andric   // by -Wpedantic via our computation.
34181ad6265SDimitry Andric   return V.first != 0 && V.first == *V.second;
3420b57cec5SDimitry Andric }
3430b57cec5SDimitry Andric 
3440b57cec5SDimitry Andric void InferPedantic::markGroup(const Record *Group) {
3450b57cec5SDimitry Andric   // If all the diagnostics and subgroups have been marked as being
3460b57cec5SDimitry Andric   // covered by -Wpedantic, increment the count of parent groups.  Once the
3470b57cec5SDimitry Andric   // group's count is equal to the number of subgroups and diagnostics in
3480b57cec5SDimitry Andric   // that group, we can safely add this group to -Wpedantic.
3490b57cec5SDimitry Andric   if (groupInPedantic(Group, /* increment */ true)) {
3500b57cec5SDimitry Andric     const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
3510b57cec5SDimitry Andric     for (unsigned i = 0, e = Parents.size(); i != e; ++i)
3520b57cec5SDimitry Andric       markGroup(Parents[i]);
3530b57cec5SDimitry Andric   }
3540b57cec5SDimitry Andric }
3550b57cec5SDimitry Andric 
3560b57cec5SDimitry Andric void InferPedantic::compute(VecOrSet DiagsInPedantic,
3570b57cec5SDimitry Andric                             VecOrSet GroupsInPedantic) {
3580b57cec5SDimitry Andric   // All extensions that are not on by default are implicitly in the
3590b57cec5SDimitry Andric   // "pedantic" group.  For those that aren't explicitly included in -Wpedantic,
3600b57cec5SDimitry Andric   // mark them for consideration to be included in -Wpedantic directly.
3610b57cec5SDimitry Andric   for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
3620b57cec5SDimitry Andric     Record *R = Diags[i];
3630b57cec5SDimitry Andric     if (isExtension(R) && isOffByDefault(R)) {
3640b57cec5SDimitry Andric       DiagsSet.insert(R);
3650b57cec5SDimitry Andric       if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) {
3660b57cec5SDimitry Andric         const Record *GroupRec = Group->getDef();
3670b57cec5SDimitry Andric         if (!isSubGroupOfGroup(GroupRec, "pedantic")) {
3680b57cec5SDimitry Andric           markGroup(GroupRec);
3690b57cec5SDimitry Andric         }
3700b57cec5SDimitry Andric       }
3710b57cec5SDimitry Andric     }
3720b57cec5SDimitry Andric   }
3730b57cec5SDimitry Andric 
3740b57cec5SDimitry Andric   // Compute the set of diagnostics that are directly in -Wpedantic.  We
3750b57cec5SDimitry Andric   // march through Diags a second time to ensure the results are emitted
3760b57cec5SDimitry Andric   // in deterministic order.
3770b57cec5SDimitry Andric   for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
3780b57cec5SDimitry Andric     Record *R = Diags[i];
3790b57cec5SDimitry Andric     if (!DiagsSet.count(R))
3800b57cec5SDimitry Andric       continue;
3810b57cec5SDimitry Andric     // Check if the group is implicitly in -Wpedantic.  If so,
3820b57cec5SDimitry Andric     // the diagnostic should not be directly included in the -Wpedantic
3830b57cec5SDimitry Andric     // diagnostic group.
3840b57cec5SDimitry Andric     if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group")))
3850b57cec5SDimitry Andric       if (groupInPedantic(Group->getDef()))
3860b57cec5SDimitry Andric         continue;
3870b57cec5SDimitry Andric 
3880b57cec5SDimitry Andric     // The diagnostic is not included in a group that is (transitively) in
3890b57cec5SDimitry Andric     // -Wpedantic.  Include it in -Wpedantic directly.
3900b57cec5SDimitry Andric     if (RecordVec *V = DiagsInPedantic.dyn_cast<RecordVec*>())
3910b57cec5SDimitry Andric       V->push_back(R);
3920b57cec5SDimitry Andric     else {
3930b57cec5SDimitry Andric       DiagsInPedantic.get<RecordSet*>()->insert(R);
3940b57cec5SDimitry Andric     }
3950b57cec5SDimitry Andric   }
3960b57cec5SDimitry Andric 
3970b57cec5SDimitry Andric   if (!GroupsInPedantic)
3980b57cec5SDimitry Andric     return;
3990b57cec5SDimitry Andric 
4000b57cec5SDimitry Andric   // Compute the set of groups that are directly in -Wpedantic.  We
4010b57cec5SDimitry Andric   // march through the groups to ensure the results are emitted
4020b57cec5SDimitry Andric   /// in a deterministc order.
4030b57cec5SDimitry Andric   for (unsigned i = 0, ei = DiagGroups.size(); i != ei; ++i) {
4040b57cec5SDimitry Andric     Record *Group = DiagGroups[i];
4050b57cec5SDimitry Andric     if (!groupInPedantic(Group))
4060b57cec5SDimitry Andric       continue;
4070b57cec5SDimitry Andric 
4080b57cec5SDimitry Andric     const std::vector<Record*> &Parents = DiagGroupParents.getParents(Group);
409bdd1243dSDimitry Andric     bool AllParentsInPedantic =
410bdd1243dSDimitry Andric         llvm::all_of(Parents, [&](Record *R) { return groupInPedantic(R); });
4110b57cec5SDimitry Andric     // If all the parents are in -Wpedantic, this means that this diagnostic
4120b57cec5SDimitry Andric     // group will be indirectly included by -Wpedantic already.  In that
4130b57cec5SDimitry Andric     // case, do not add it directly to -Wpedantic.  If the group has no
4140b57cec5SDimitry Andric     // parents, obviously it should go into -Wpedantic.
415bdd1243dSDimitry Andric     if (Parents.size() > 0 && AllParentsInPedantic)
4160b57cec5SDimitry Andric       continue;
4170b57cec5SDimitry Andric 
4180b57cec5SDimitry Andric     if (RecordVec *V = GroupsInPedantic.dyn_cast<RecordVec*>())
4190b57cec5SDimitry Andric       V->push_back(Group);
4200b57cec5SDimitry Andric     else {
4210b57cec5SDimitry Andric       GroupsInPedantic.get<RecordSet*>()->insert(Group);
4220b57cec5SDimitry Andric     }
4230b57cec5SDimitry Andric   }
4240b57cec5SDimitry Andric }
4250b57cec5SDimitry Andric 
4260b57cec5SDimitry Andric namespace {
4270b57cec5SDimitry Andric enum PieceKind {
4280b57cec5SDimitry Andric   MultiPieceClass,
4290b57cec5SDimitry Andric   TextPieceClass,
4300b57cec5SDimitry Andric   PlaceholderPieceClass,
4310b57cec5SDimitry Andric   SelectPieceClass,
4320b57cec5SDimitry Andric   PluralPieceClass,
4330b57cec5SDimitry Andric   DiffPieceClass,
4340b57cec5SDimitry Andric   SubstitutionPieceClass,
4350b57cec5SDimitry Andric };
4360b57cec5SDimitry Andric 
4370b57cec5SDimitry Andric enum ModifierType {
4380b57cec5SDimitry Andric   MT_Unknown,
4390b57cec5SDimitry Andric   MT_Placeholder,
4400b57cec5SDimitry Andric   MT_Select,
4410b57cec5SDimitry Andric   MT_Sub,
4420b57cec5SDimitry Andric   MT_Plural,
4430b57cec5SDimitry Andric   MT_Diff,
4440b57cec5SDimitry Andric   MT_Ordinal,
4450b57cec5SDimitry Andric   MT_S,
4460b57cec5SDimitry Andric   MT_Q,
4470b57cec5SDimitry Andric   MT_ObjCClass,
4480b57cec5SDimitry Andric   MT_ObjCInstance,
4490b57cec5SDimitry Andric };
4500b57cec5SDimitry Andric 
4510b57cec5SDimitry Andric static StringRef getModifierName(ModifierType MT) {
4520b57cec5SDimitry Andric   switch (MT) {
4530b57cec5SDimitry Andric   case MT_Select:
4540b57cec5SDimitry Andric     return "select";
4550b57cec5SDimitry Andric   case MT_Sub:
4560b57cec5SDimitry Andric     return "sub";
4570b57cec5SDimitry Andric   case MT_Diff:
4580b57cec5SDimitry Andric     return "diff";
4590b57cec5SDimitry Andric   case MT_Plural:
4600b57cec5SDimitry Andric     return "plural";
4610b57cec5SDimitry Andric   case MT_Ordinal:
4620b57cec5SDimitry Andric     return "ordinal";
4630b57cec5SDimitry Andric   case MT_S:
4640b57cec5SDimitry Andric     return "s";
4650b57cec5SDimitry Andric   case MT_Q:
4660b57cec5SDimitry Andric     return "q";
4670b57cec5SDimitry Andric   case MT_Placeholder:
4680b57cec5SDimitry Andric     return "";
4690b57cec5SDimitry Andric   case MT_ObjCClass:
4700b57cec5SDimitry Andric     return "objcclass";
4710b57cec5SDimitry Andric   case MT_ObjCInstance:
4720b57cec5SDimitry Andric     return "objcinstance";
4730b57cec5SDimitry Andric   case MT_Unknown:
4740b57cec5SDimitry Andric     llvm_unreachable("invalid modifier type");
4750b57cec5SDimitry Andric   }
4760b57cec5SDimitry Andric   // Unhandled case
4770b57cec5SDimitry Andric   llvm_unreachable("invalid modifier type");
4780b57cec5SDimitry Andric }
4790b57cec5SDimitry Andric 
4800b57cec5SDimitry Andric struct Piece {
4810b57cec5SDimitry Andric   // This type and its derived classes are move-only.
4820b57cec5SDimitry Andric   Piece(PieceKind Kind) : ClassKind(Kind) {}
4830b57cec5SDimitry Andric   Piece(Piece const &O) = delete;
4840b57cec5SDimitry Andric   Piece &operator=(Piece const &) = delete;
4850b57cec5SDimitry Andric   virtual ~Piece() {}
4860b57cec5SDimitry Andric 
4870b57cec5SDimitry Andric   PieceKind getPieceClass() const { return ClassKind; }
4880b57cec5SDimitry Andric   static bool classof(const Piece *) { return true; }
4890b57cec5SDimitry Andric 
4900b57cec5SDimitry Andric private:
4910b57cec5SDimitry Andric   PieceKind ClassKind;
4920b57cec5SDimitry Andric };
4930b57cec5SDimitry Andric 
4940b57cec5SDimitry Andric struct MultiPiece : Piece {
4950b57cec5SDimitry Andric   MultiPiece() : Piece(MultiPieceClass) {}
4960b57cec5SDimitry Andric   MultiPiece(std::vector<Piece *> Pieces)
4970b57cec5SDimitry Andric       : Piece(MultiPieceClass), Pieces(std::move(Pieces)) {}
4980b57cec5SDimitry Andric 
4990b57cec5SDimitry Andric   std::vector<Piece *> Pieces;
5000b57cec5SDimitry Andric 
5010b57cec5SDimitry Andric   static bool classof(const Piece *P) {
5020b57cec5SDimitry Andric     return P->getPieceClass() == MultiPieceClass;
5030b57cec5SDimitry Andric   }
5040b57cec5SDimitry Andric };
5050b57cec5SDimitry Andric 
5060b57cec5SDimitry Andric struct TextPiece : Piece {
5070b57cec5SDimitry Andric   StringRef Role;
5080b57cec5SDimitry Andric   std::string Text;
5090b57cec5SDimitry Andric   TextPiece(StringRef Text, StringRef Role = "")
5100b57cec5SDimitry Andric       : Piece(TextPieceClass), Role(Role), Text(Text.str()) {}
5110b57cec5SDimitry Andric 
5120b57cec5SDimitry Andric   static bool classof(const Piece *P) {
5130b57cec5SDimitry Andric     return P->getPieceClass() == TextPieceClass;
5140b57cec5SDimitry Andric   }
5150b57cec5SDimitry Andric };
5160b57cec5SDimitry Andric 
5170b57cec5SDimitry Andric struct PlaceholderPiece : Piece {
5180b57cec5SDimitry Andric   ModifierType Kind;
5190b57cec5SDimitry Andric   int Index;
5200b57cec5SDimitry Andric   PlaceholderPiece(ModifierType Kind, int Index)
5210b57cec5SDimitry Andric       : Piece(PlaceholderPieceClass), Kind(Kind), Index(Index) {}
5220b57cec5SDimitry Andric 
5230b57cec5SDimitry Andric   static bool classof(const Piece *P) {
5240b57cec5SDimitry Andric     return P->getPieceClass() == PlaceholderPieceClass;
5250b57cec5SDimitry Andric   }
5260b57cec5SDimitry Andric };
5270b57cec5SDimitry Andric 
5280b57cec5SDimitry Andric struct SelectPiece : Piece {
5290b57cec5SDimitry Andric protected:
5300b57cec5SDimitry Andric   SelectPiece(PieceKind Kind, ModifierType ModKind)
5310b57cec5SDimitry Andric       : Piece(Kind), ModKind(ModKind) {}
5320b57cec5SDimitry Andric 
5330b57cec5SDimitry Andric public:
5340b57cec5SDimitry Andric   SelectPiece(ModifierType ModKind) : SelectPiece(SelectPieceClass, ModKind) {}
5350b57cec5SDimitry Andric 
5360b57cec5SDimitry Andric   ModifierType ModKind;
5370b57cec5SDimitry Andric   std::vector<Piece *> Options;
538480093f4SDimitry Andric   int Index = 0;
5390b57cec5SDimitry Andric 
5400b57cec5SDimitry Andric   static bool classof(const Piece *P) {
5410b57cec5SDimitry Andric     return P->getPieceClass() == SelectPieceClass ||
5420b57cec5SDimitry Andric            P->getPieceClass() == PluralPieceClass;
5430b57cec5SDimitry Andric   }
5440b57cec5SDimitry Andric };
5450b57cec5SDimitry Andric 
5460b57cec5SDimitry Andric struct PluralPiece : SelectPiece {
5470b57cec5SDimitry Andric   PluralPiece() : SelectPiece(PluralPieceClass, MT_Plural) {}
5480b57cec5SDimitry Andric 
5490b57cec5SDimitry Andric   std::vector<Piece *> OptionPrefixes;
550480093f4SDimitry Andric   int Index = 0;
5510b57cec5SDimitry Andric 
5520b57cec5SDimitry Andric   static bool classof(const Piece *P) {
5530b57cec5SDimitry Andric     return P->getPieceClass() == PluralPieceClass;
5540b57cec5SDimitry Andric   }
5550b57cec5SDimitry Andric };
5560b57cec5SDimitry Andric 
5570b57cec5SDimitry Andric struct DiffPiece : Piece {
5580b57cec5SDimitry Andric   DiffPiece() : Piece(DiffPieceClass) {}
5590b57cec5SDimitry Andric 
560fe6060f1SDimitry Andric   Piece *Parts[4] = {};
5610b57cec5SDimitry Andric   int Indexes[2] = {};
5620b57cec5SDimitry Andric 
5630b57cec5SDimitry Andric   static bool classof(const Piece *P) {
5640b57cec5SDimitry Andric     return P->getPieceClass() == DiffPieceClass;
5650b57cec5SDimitry Andric   }
5660b57cec5SDimitry Andric };
5670b57cec5SDimitry Andric 
5680b57cec5SDimitry Andric struct SubstitutionPiece : Piece {
5690b57cec5SDimitry Andric   SubstitutionPiece() : Piece(SubstitutionPieceClass) {}
5700b57cec5SDimitry Andric 
5710b57cec5SDimitry Andric   std::string Name;
5720b57cec5SDimitry Andric   std::vector<int> Modifiers;
5730b57cec5SDimitry Andric 
5740b57cec5SDimitry Andric   static bool classof(const Piece *P) {
5750b57cec5SDimitry Andric     return P->getPieceClass() == SubstitutionPieceClass;
5760b57cec5SDimitry Andric   }
5770b57cec5SDimitry Andric };
5780b57cec5SDimitry Andric 
5790b57cec5SDimitry Andric /// Diagnostic text, parsed into pieces.
5800b57cec5SDimitry Andric 
5810b57cec5SDimitry Andric 
5820b57cec5SDimitry Andric struct DiagnosticTextBuilder {
5830b57cec5SDimitry Andric   DiagnosticTextBuilder(DiagnosticTextBuilder const &) = delete;
5840b57cec5SDimitry Andric   DiagnosticTextBuilder &operator=(DiagnosticTextBuilder const &) = delete;
5850b57cec5SDimitry Andric 
5860b57cec5SDimitry Andric   DiagnosticTextBuilder(RecordKeeper &Records) {
5870b57cec5SDimitry Andric     // Build up the list of substitution records.
5880b57cec5SDimitry Andric     for (auto *S : Records.getAllDerivedDefinitions("TextSubstitution")) {
5890b57cec5SDimitry Andric       EvaluatingRecordGuard Guard(&EvaluatingRecord, S);
5900b57cec5SDimitry Andric       Substitutions.try_emplace(
5910b57cec5SDimitry Andric           S->getName(), DiagText(*this, S->getValueAsString("Substitution")));
5920b57cec5SDimitry Andric     }
5930b57cec5SDimitry Andric 
5940b57cec5SDimitry Andric     // Check that no diagnostic definitions have the same name as a
5950b57cec5SDimitry Andric     // substitution.
5960b57cec5SDimitry Andric     for (Record *Diag : Records.getAllDerivedDefinitions("Diagnostic")) {
5970b57cec5SDimitry Andric       StringRef Name = Diag->getName();
5980b57cec5SDimitry Andric       if (Substitutions.count(Name))
5990b57cec5SDimitry Andric         llvm::PrintFatalError(
6000b57cec5SDimitry Andric             Diag->getLoc(),
6010b57cec5SDimitry Andric             "Diagnostic '" + Name +
6020b57cec5SDimitry Andric                 "' has same name as TextSubstitution definition");
6030b57cec5SDimitry Andric     }
6040b57cec5SDimitry Andric   }
6050b57cec5SDimitry Andric 
6060b57cec5SDimitry Andric   std::vector<std::string> buildForDocumentation(StringRef Role,
6070b57cec5SDimitry Andric                                                  const Record *R);
6080b57cec5SDimitry Andric   std::string buildForDefinition(const Record *R);
6090b57cec5SDimitry Andric 
6100b57cec5SDimitry Andric   Piece *getSubstitution(SubstitutionPiece *S) const {
6110b57cec5SDimitry Andric     auto It = Substitutions.find(S->Name);
6120b57cec5SDimitry Andric     if (It == Substitutions.end())
6130b57cec5SDimitry Andric       PrintFatalError("Failed to find substitution with name: " + S->Name);
6140b57cec5SDimitry Andric     return It->second.Root;
6150b57cec5SDimitry Andric   }
6160b57cec5SDimitry Andric 
617349cc55cSDimitry Andric   [[noreturn]] void PrintFatalError(llvm::Twine const &Msg) const {
6180b57cec5SDimitry Andric     assert(EvaluatingRecord && "not evaluating a record?");
6190b57cec5SDimitry Andric     llvm::PrintFatalError(EvaluatingRecord->getLoc(), Msg);
6200b57cec5SDimitry Andric   }
6210b57cec5SDimitry Andric 
6220b57cec5SDimitry Andric private:
6230b57cec5SDimitry Andric   struct DiagText {
6240b57cec5SDimitry Andric     DiagnosticTextBuilder &Builder;
6250b57cec5SDimitry Andric     std::vector<Piece *> AllocatedPieces;
6260b57cec5SDimitry Andric     Piece *Root = nullptr;
6270b57cec5SDimitry Andric 
6280b57cec5SDimitry Andric     template <class T, class... Args> T *New(Args &&... args) {
6290b57cec5SDimitry Andric       static_assert(std::is_base_of<Piece, T>::value, "must be piece");
6300b57cec5SDimitry Andric       T *Mem = new T(std::forward<Args>(args)...);
6310b57cec5SDimitry Andric       AllocatedPieces.push_back(Mem);
6320b57cec5SDimitry Andric       return Mem;
6330b57cec5SDimitry Andric     }
6340b57cec5SDimitry Andric 
6350b57cec5SDimitry Andric     DiagText(DiagnosticTextBuilder &Builder, StringRef Text)
636fe6060f1SDimitry Andric         : Builder(Builder), Root(parseDiagText(Text, StopAt::End)) {}
6370b57cec5SDimitry Andric 
638fe6060f1SDimitry Andric     enum class StopAt {
639fe6060f1SDimitry Andric       // Parse until the end of the string.
640fe6060f1SDimitry Andric       End,
641fe6060f1SDimitry Andric       // Additionally stop if we hit a non-nested '|' or '}'.
642fe6060f1SDimitry Andric       PipeOrCloseBrace,
643fe6060f1SDimitry Andric       // Additionally stop if we hit a non-nested '$'.
644fe6060f1SDimitry Andric       Dollar,
645fe6060f1SDimitry Andric     };
646fe6060f1SDimitry Andric 
647fe6060f1SDimitry Andric     Piece *parseDiagText(StringRef &Text, StopAt Stop);
6480b57cec5SDimitry Andric     int parseModifier(StringRef &) const;
6490b57cec5SDimitry Andric 
6500b57cec5SDimitry Andric   public:
6510b57cec5SDimitry Andric     DiagText(DiagText &&O) noexcept
6520b57cec5SDimitry Andric         : Builder(O.Builder), AllocatedPieces(std::move(O.AllocatedPieces)),
6530b57cec5SDimitry Andric           Root(O.Root) {
6540b57cec5SDimitry Andric       O.Root = nullptr;
6550b57cec5SDimitry Andric     }
65606c3fb27SDimitry Andric     // The move assignment operator is defined as deleted pending further
65706c3fb27SDimitry Andric     // motivation.
65806c3fb27SDimitry Andric     DiagText &operator=(DiagText &&) = delete;
65906c3fb27SDimitry Andric 
66006c3fb27SDimitry Andric     // The copy constrcutor and copy assignment operator is defined as deleted
66106c3fb27SDimitry Andric     // pending further motivation.
66206c3fb27SDimitry Andric     DiagText(const DiagText &) = delete;
66306c3fb27SDimitry Andric     DiagText &operator=(const DiagText &) = delete;
6640b57cec5SDimitry Andric 
6650b57cec5SDimitry Andric     ~DiagText() {
6660b57cec5SDimitry Andric       for (Piece *P : AllocatedPieces)
6670b57cec5SDimitry Andric         delete P;
6680b57cec5SDimitry Andric     }
6690b57cec5SDimitry Andric   };
6700b57cec5SDimitry Andric 
6710b57cec5SDimitry Andric private:
6720b57cec5SDimitry Andric   const Record *EvaluatingRecord = nullptr;
6730b57cec5SDimitry Andric   struct EvaluatingRecordGuard {
6740b57cec5SDimitry Andric     EvaluatingRecordGuard(const Record **Dest, const Record *New)
6750b57cec5SDimitry Andric         : Dest(Dest), Old(*Dest) {
6760b57cec5SDimitry Andric       *Dest = New;
6770b57cec5SDimitry Andric     }
6780b57cec5SDimitry Andric     ~EvaluatingRecordGuard() { *Dest = Old; }
6790b57cec5SDimitry Andric     const Record **Dest;
6800b57cec5SDimitry Andric     const Record *Old;
6810b57cec5SDimitry Andric   };
6820b57cec5SDimitry Andric 
6830b57cec5SDimitry Andric   StringMap<DiagText> Substitutions;
6840b57cec5SDimitry Andric };
6850b57cec5SDimitry Andric 
6860b57cec5SDimitry Andric template <class Derived> struct DiagTextVisitor {
687bdd1243dSDimitry Andric   using ModifierMappingsType = std::optional<std::vector<int>>;
6880b57cec5SDimitry Andric 
6890b57cec5SDimitry Andric private:
6900b57cec5SDimitry Andric   Derived &getDerived() { return static_cast<Derived &>(*this); }
6910b57cec5SDimitry Andric 
6920b57cec5SDimitry Andric public:
6930b57cec5SDimitry Andric   std::vector<int>
6940b57cec5SDimitry Andric   getSubstitutionMappings(SubstitutionPiece *P,
6950b57cec5SDimitry Andric                           const ModifierMappingsType &Mappings) const {
6960b57cec5SDimitry Andric     std::vector<int> NewMappings;
6970b57cec5SDimitry Andric     for (int Idx : P->Modifiers)
6980b57cec5SDimitry Andric       NewMappings.push_back(mapIndex(Idx, Mappings));
6990b57cec5SDimitry Andric     return NewMappings;
7000b57cec5SDimitry Andric   }
7010b57cec5SDimitry Andric 
7020b57cec5SDimitry Andric   struct SubstitutionContext {
7030b57cec5SDimitry Andric     SubstitutionContext(DiagTextVisitor &Visitor, SubstitutionPiece *P)
7040b57cec5SDimitry Andric         : Visitor(Visitor) {
7050b57cec5SDimitry Andric       Substitution = Visitor.Builder.getSubstitution(P);
7060b57cec5SDimitry Andric       OldMappings = std::move(Visitor.ModifierMappings);
7070b57cec5SDimitry Andric       std::vector<int> NewMappings =
7080b57cec5SDimitry Andric           Visitor.getSubstitutionMappings(P, OldMappings);
7090b57cec5SDimitry Andric       Visitor.ModifierMappings = std::move(NewMappings);
7100b57cec5SDimitry Andric     }
7110b57cec5SDimitry Andric 
7120b57cec5SDimitry Andric     ~SubstitutionContext() {
7130b57cec5SDimitry Andric       Visitor.ModifierMappings = std::move(OldMappings);
7140b57cec5SDimitry Andric     }
7150b57cec5SDimitry Andric 
7160b57cec5SDimitry Andric   private:
7170b57cec5SDimitry Andric     DiagTextVisitor &Visitor;
718bdd1243dSDimitry Andric     std::optional<std::vector<int>> OldMappings;
7190b57cec5SDimitry Andric 
7200b57cec5SDimitry Andric   public:
7210b57cec5SDimitry Andric     Piece *Substitution;
7220b57cec5SDimitry Andric   };
7230b57cec5SDimitry Andric 
7240b57cec5SDimitry Andric public:
7250b57cec5SDimitry Andric   DiagTextVisitor(DiagnosticTextBuilder &Builder) : Builder(Builder) {}
7260b57cec5SDimitry Andric 
7270b57cec5SDimitry Andric   void Visit(Piece *P) {
7280b57cec5SDimitry Andric     switch (P->getPieceClass()) {
7290b57cec5SDimitry Andric #define CASE(T)                                                                \
7300b57cec5SDimitry Andric   case T##PieceClass:                                                          \
7310b57cec5SDimitry Andric     return getDerived().Visit##T(static_cast<T##Piece *>(P))
7320b57cec5SDimitry Andric       CASE(Multi);
7330b57cec5SDimitry Andric       CASE(Text);
7340b57cec5SDimitry Andric       CASE(Placeholder);
7350b57cec5SDimitry Andric       CASE(Select);
7360b57cec5SDimitry Andric       CASE(Plural);
7370b57cec5SDimitry Andric       CASE(Diff);
7380b57cec5SDimitry Andric       CASE(Substitution);
7390b57cec5SDimitry Andric #undef CASE
7400b57cec5SDimitry Andric     }
7410b57cec5SDimitry Andric   }
7420b57cec5SDimitry Andric 
7430b57cec5SDimitry Andric   void VisitSubstitution(SubstitutionPiece *P) {
7440b57cec5SDimitry Andric     SubstitutionContext Guard(*this, P);
7450b57cec5SDimitry Andric     Visit(Guard.Substitution);
7460b57cec5SDimitry Andric   }
7470b57cec5SDimitry Andric 
7480b57cec5SDimitry Andric   int mapIndex(int Idx,
7490b57cec5SDimitry Andric                     ModifierMappingsType const &ModifierMappings) const {
7500b57cec5SDimitry Andric     if (!ModifierMappings)
7510b57cec5SDimitry Andric       return Idx;
7520b57cec5SDimitry Andric     if (ModifierMappings->size() <= static_cast<unsigned>(Idx))
7530b57cec5SDimitry Andric       Builder.PrintFatalError("Modifier value '" + std::to_string(Idx) +
7540b57cec5SDimitry Andric                               "' is not valid for this mapping (has " +
7550b57cec5SDimitry Andric                               std::to_string(ModifierMappings->size()) +
7560b57cec5SDimitry Andric                               " mappings)");
7570b57cec5SDimitry Andric     return (*ModifierMappings)[Idx];
7580b57cec5SDimitry Andric   }
7590b57cec5SDimitry Andric 
7600b57cec5SDimitry Andric   int mapIndex(int Idx) const {
7610b57cec5SDimitry Andric     return mapIndex(Idx, ModifierMappings);
7620b57cec5SDimitry Andric   }
7630b57cec5SDimitry Andric 
7640b57cec5SDimitry Andric protected:
7650b57cec5SDimitry Andric   DiagnosticTextBuilder &Builder;
7660b57cec5SDimitry Andric   ModifierMappingsType ModifierMappings;
7670b57cec5SDimitry Andric };
7680b57cec5SDimitry Andric 
7690b57cec5SDimitry Andric void escapeRST(StringRef Str, std::string &Out) {
7700b57cec5SDimitry Andric   for (auto K : Str) {
7710b57cec5SDimitry Andric     if (StringRef("`*|_[]\\").count(K))
7720b57cec5SDimitry Andric       Out.push_back('\\');
7730b57cec5SDimitry Andric     Out.push_back(K);
7740b57cec5SDimitry Andric   }
7750b57cec5SDimitry Andric }
7760b57cec5SDimitry Andric 
7770b57cec5SDimitry Andric template <typename It> void padToSameLength(It Begin, It End) {
7780b57cec5SDimitry Andric   size_t Width = 0;
7790b57cec5SDimitry Andric   for (It I = Begin; I != End; ++I)
7800b57cec5SDimitry Andric     Width = std::max(Width, I->size());
7810b57cec5SDimitry Andric   for (It I = Begin; I != End; ++I)
7820b57cec5SDimitry Andric     (*I) += std::string(Width - I->size(), ' ');
7830b57cec5SDimitry Andric }
7840b57cec5SDimitry Andric 
7850b57cec5SDimitry Andric template <typename It> void makeTableRows(It Begin, It End) {
7860b57cec5SDimitry Andric   if (Begin == End)
7870b57cec5SDimitry Andric     return;
7880b57cec5SDimitry Andric   padToSameLength(Begin, End);
7890b57cec5SDimitry Andric   for (It I = Begin; I != End; ++I)
7900b57cec5SDimitry Andric     *I = "|" + *I + "|";
7910b57cec5SDimitry Andric }
7920b57cec5SDimitry Andric 
7930b57cec5SDimitry Andric void makeRowSeparator(std::string &Str) {
7940b57cec5SDimitry Andric   for (char &K : Str)
7950b57cec5SDimitry Andric     K = (K == '|' ? '+' : '-');
7960b57cec5SDimitry Andric }
7970b57cec5SDimitry Andric 
7980b57cec5SDimitry Andric struct DiagTextDocPrinter : DiagTextVisitor<DiagTextDocPrinter> {
7990b57cec5SDimitry Andric   using BaseTy = DiagTextVisitor<DiagTextDocPrinter>;
8000b57cec5SDimitry Andric   DiagTextDocPrinter(DiagnosticTextBuilder &Builder,
8010b57cec5SDimitry Andric                      std::vector<std::string> &RST)
8020b57cec5SDimitry Andric       : BaseTy(Builder), RST(RST) {}
8030b57cec5SDimitry Andric 
8040b57cec5SDimitry Andric   void gatherNodes(
8050b57cec5SDimitry Andric       Piece *OrigP, const ModifierMappingsType &CurrentMappings,
8060b57cec5SDimitry Andric       std::vector<std::pair<Piece *, ModifierMappingsType>> &Pieces) const {
8070b57cec5SDimitry Andric     if (auto *Sub = dyn_cast<SubstitutionPiece>(OrigP)) {
8080b57cec5SDimitry Andric       ModifierMappingsType NewMappings =
8090b57cec5SDimitry Andric           getSubstitutionMappings(Sub, CurrentMappings);
8100b57cec5SDimitry Andric       return gatherNodes(Builder.getSubstitution(Sub), NewMappings, Pieces);
8110b57cec5SDimitry Andric     }
8120b57cec5SDimitry Andric     if (auto *MD = dyn_cast<MultiPiece>(OrigP)) {
8130b57cec5SDimitry Andric       for (Piece *Node : MD->Pieces)
8140b57cec5SDimitry Andric         gatherNodes(Node, CurrentMappings, Pieces);
8150b57cec5SDimitry Andric       return;
8160b57cec5SDimitry Andric     }
8170b57cec5SDimitry Andric     Pieces.push_back(std::make_pair(OrigP, CurrentMappings));
8180b57cec5SDimitry Andric   }
8190b57cec5SDimitry Andric 
8200b57cec5SDimitry Andric   void VisitMulti(MultiPiece *P) {
8210b57cec5SDimitry Andric     if (P->Pieces.empty()) {
8220b57cec5SDimitry Andric       RST.push_back("");
8230b57cec5SDimitry Andric       return;
8240b57cec5SDimitry Andric     }
8250b57cec5SDimitry Andric 
8260b57cec5SDimitry Andric     if (P->Pieces.size() == 1)
8270b57cec5SDimitry Andric       return Visit(P->Pieces[0]);
8280b57cec5SDimitry Andric 
8290b57cec5SDimitry Andric     // Flatten the list of nodes, replacing any substitution pieces with the
8300b57cec5SDimitry Andric     // recursively flattened substituted node.
8310b57cec5SDimitry Andric     std::vector<std::pair<Piece *, ModifierMappingsType>> Pieces;
8320b57cec5SDimitry Andric     gatherNodes(P, ModifierMappings, Pieces);
8330b57cec5SDimitry Andric 
8340b57cec5SDimitry Andric     std::string EmptyLinePrefix;
8350b57cec5SDimitry Andric     size_t Start = RST.size();
8360b57cec5SDimitry Andric     bool HasMultipleLines = true;
8370b57cec5SDimitry Andric     for (const std::pair<Piece *, ModifierMappingsType> &NodePair : Pieces) {
8380b57cec5SDimitry Andric       std::vector<std::string> Lines;
8390b57cec5SDimitry Andric       DiagTextDocPrinter Visitor{Builder, Lines};
8400b57cec5SDimitry Andric       Visitor.ModifierMappings = NodePair.second;
8410b57cec5SDimitry Andric       Visitor.Visit(NodePair.first);
8420b57cec5SDimitry Andric 
8430b57cec5SDimitry Andric       if (Lines.empty())
8440b57cec5SDimitry Andric         continue;
8450b57cec5SDimitry Andric 
8460b57cec5SDimitry Andric       // We need a vertical separator if either this or the previous piece is a
8470b57cec5SDimitry Andric       // multi-line piece, or this is the last piece.
8480b57cec5SDimitry Andric       const char *Separator = (Lines.size() > 1 || HasMultipleLines) ? "|" : "";
8490b57cec5SDimitry Andric       HasMultipleLines = Lines.size() > 1;
8500b57cec5SDimitry Andric 
8510b57cec5SDimitry Andric       if (Start + Lines.size() > RST.size())
8520b57cec5SDimitry Andric         RST.resize(Start + Lines.size(), EmptyLinePrefix);
8530b57cec5SDimitry Andric 
8540b57cec5SDimitry Andric       padToSameLength(Lines.begin(), Lines.end());
8550b57cec5SDimitry Andric       for (size_t I = 0; I != Lines.size(); ++I)
8560b57cec5SDimitry Andric         RST[Start + I] += Separator + Lines[I];
8570b57cec5SDimitry Andric       std::string Empty(Lines[0].size(), ' ');
8580b57cec5SDimitry Andric       for (size_t I = Start + Lines.size(); I != RST.size(); ++I)
8590b57cec5SDimitry Andric         RST[I] += Separator + Empty;
8600b57cec5SDimitry Andric       EmptyLinePrefix += Separator + Empty;
8610b57cec5SDimitry Andric     }
8620b57cec5SDimitry Andric     for (size_t I = Start; I != RST.size(); ++I)
8630b57cec5SDimitry Andric       RST[I] += "|";
8640b57cec5SDimitry Andric     EmptyLinePrefix += "|";
8650b57cec5SDimitry Andric 
8660b57cec5SDimitry Andric     makeRowSeparator(EmptyLinePrefix);
8670b57cec5SDimitry Andric     RST.insert(RST.begin() + Start, EmptyLinePrefix);
8680b57cec5SDimitry Andric     RST.insert(RST.end(), EmptyLinePrefix);
8690b57cec5SDimitry Andric   }
8700b57cec5SDimitry Andric 
8710b57cec5SDimitry Andric   void VisitText(TextPiece *P) {
8720b57cec5SDimitry Andric     RST.push_back("");
8730b57cec5SDimitry Andric     auto &S = RST.back();
8740b57cec5SDimitry Andric 
8750b57cec5SDimitry Andric     StringRef T = P->Text;
876*0fca6ea1SDimitry Andric     while (T.consume_front(" "))
8770b57cec5SDimitry Andric       RST.back() += " |nbsp| ";
8780b57cec5SDimitry Andric 
8790b57cec5SDimitry Andric     std::string Suffix;
880*0fca6ea1SDimitry Andric     while (T.consume_back(" "))
8810b57cec5SDimitry Andric       Suffix += " |nbsp| ";
8820b57cec5SDimitry Andric 
8830b57cec5SDimitry Andric     if (!T.empty()) {
8840b57cec5SDimitry Andric       S += ':';
8850b57cec5SDimitry Andric       S += P->Role;
8860b57cec5SDimitry Andric       S += ":`";
8870b57cec5SDimitry Andric       escapeRST(T, S);
8880b57cec5SDimitry Andric       S += '`';
8890b57cec5SDimitry Andric     }
8900b57cec5SDimitry Andric 
8910b57cec5SDimitry Andric     S += Suffix;
8920b57cec5SDimitry Andric   }
8930b57cec5SDimitry Andric 
8940b57cec5SDimitry Andric   void VisitPlaceholder(PlaceholderPiece *P) {
8950b57cec5SDimitry Andric     RST.push_back(std::string(":placeholder:`") +
8960b57cec5SDimitry Andric                   char('A' + mapIndex(P->Index)) + "`");
8970b57cec5SDimitry Andric   }
8980b57cec5SDimitry Andric 
8990b57cec5SDimitry Andric   void VisitSelect(SelectPiece *P) {
9000b57cec5SDimitry Andric     std::vector<size_t> SeparatorIndexes;
9010b57cec5SDimitry Andric     SeparatorIndexes.push_back(RST.size());
9020b57cec5SDimitry Andric     RST.emplace_back();
9030b57cec5SDimitry Andric     for (auto *O : P->Options) {
9040b57cec5SDimitry Andric       Visit(O);
9050b57cec5SDimitry Andric       SeparatorIndexes.push_back(RST.size());
9060b57cec5SDimitry Andric       RST.emplace_back();
9070b57cec5SDimitry Andric     }
9080b57cec5SDimitry Andric 
9090b57cec5SDimitry Andric     makeTableRows(RST.begin() + SeparatorIndexes.front(),
9100b57cec5SDimitry Andric                   RST.begin() + SeparatorIndexes.back() + 1);
9110b57cec5SDimitry Andric     for (size_t I : SeparatorIndexes)
9120b57cec5SDimitry Andric       makeRowSeparator(RST[I]);
9130b57cec5SDimitry Andric   }
9140b57cec5SDimitry Andric 
9150b57cec5SDimitry Andric   void VisitPlural(PluralPiece *P) { VisitSelect(P); }
9160b57cec5SDimitry Andric 
917fe6060f1SDimitry Andric   void VisitDiff(DiffPiece *P) {
918fe6060f1SDimitry Andric     // Render %diff{a $ b $ c|d}e,f as %select{a %e b %f c|d}.
919fe6060f1SDimitry Andric     PlaceholderPiece E(MT_Placeholder, P->Indexes[0]);
920fe6060f1SDimitry Andric     PlaceholderPiece F(MT_Placeholder, P->Indexes[1]);
921fe6060f1SDimitry Andric 
922fe6060f1SDimitry Andric     MultiPiece FirstOption;
923fe6060f1SDimitry Andric     FirstOption.Pieces.push_back(P->Parts[0]);
924fe6060f1SDimitry Andric     FirstOption.Pieces.push_back(&E);
925fe6060f1SDimitry Andric     FirstOption.Pieces.push_back(P->Parts[1]);
926fe6060f1SDimitry Andric     FirstOption.Pieces.push_back(&F);
927fe6060f1SDimitry Andric     FirstOption.Pieces.push_back(P->Parts[2]);
928fe6060f1SDimitry Andric 
929fe6060f1SDimitry Andric     SelectPiece Select(MT_Diff);
930fe6060f1SDimitry Andric     Select.Options.push_back(&FirstOption);
931fe6060f1SDimitry Andric     Select.Options.push_back(P->Parts[3]);
932fe6060f1SDimitry Andric 
933fe6060f1SDimitry Andric     VisitSelect(&Select);
934fe6060f1SDimitry Andric   }
9350b57cec5SDimitry Andric 
9360b57cec5SDimitry Andric   std::vector<std::string> &RST;
9370b57cec5SDimitry Andric };
9380b57cec5SDimitry Andric 
9390b57cec5SDimitry Andric struct DiagTextPrinter : DiagTextVisitor<DiagTextPrinter> {
9400b57cec5SDimitry Andric public:
9410b57cec5SDimitry Andric   using BaseTy = DiagTextVisitor<DiagTextPrinter>;
9420b57cec5SDimitry Andric   DiagTextPrinter(DiagnosticTextBuilder &Builder, std::string &Result)
9430b57cec5SDimitry Andric       : BaseTy(Builder), Result(Result) {}
9440b57cec5SDimitry Andric 
9450b57cec5SDimitry Andric   void VisitMulti(MultiPiece *P) {
9460b57cec5SDimitry Andric     for (auto *Child : P->Pieces)
9470b57cec5SDimitry Andric       Visit(Child);
9480b57cec5SDimitry Andric   }
9490b57cec5SDimitry Andric   void VisitText(TextPiece *P) { Result += P->Text; }
9500b57cec5SDimitry Andric   void VisitPlaceholder(PlaceholderPiece *P) {
9510b57cec5SDimitry Andric     Result += "%";
9520b57cec5SDimitry Andric     Result += getModifierName(P->Kind);
9530b57cec5SDimitry Andric     addInt(mapIndex(P->Index));
9540b57cec5SDimitry Andric   }
9550b57cec5SDimitry Andric   void VisitSelect(SelectPiece *P) {
9560b57cec5SDimitry Andric     Result += "%";
9570b57cec5SDimitry Andric     Result += getModifierName(P->ModKind);
9580b57cec5SDimitry Andric     if (P->ModKind == MT_Select) {
9590b57cec5SDimitry Andric       Result += "{";
9600b57cec5SDimitry Andric       for (auto *D : P->Options) {
9610b57cec5SDimitry Andric         Visit(D);
9620b57cec5SDimitry Andric         Result += '|';
9630b57cec5SDimitry Andric       }
9640b57cec5SDimitry Andric       if (!P->Options.empty())
9650b57cec5SDimitry Andric         Result.erase(--Result.end());
9660b57cec5SDimitry Andric       Result += '}';
9670b57cec5SDimitry Andric     }
9680b57cec5SDimitry Andric     addInt(mapIndex(P->Index));
9690b57cec5SDimitry Andric   }
9700b57cec5SDimitry Andric 
9710b57cec5SDimitry Andric   void VisitPlural(PluralPiece *P) {
9720b57cec5SDimitry Andric     Result += "%plural{";
9730b57cec5SDimitry Andric     assert(P->Options.size() == P->OptionPrefixes.size());
9740b57cec5SDimitry Andric     for (unsigned I = 0, End = P->Options.size(); I < End; ++I) {
9750b57cec5SDimitry Andric       if (P->OptionPrefixes[I])
9760b57cec5SDimitry Andric         Visit(P->OptionPrefixes[I]);
9770b57cec5SDimitry Andric       Visit(P->Options[I]);
9780b57cec5SDimitry Andric       Result += "|";
9790b57cec5SDimitry Andric     }
9800b57cec5SDimitry Andric     if (!P->Options.empty())
9810b57cec5SDimitry Andric       Result.erase(--Result.end());
9820b57cec5SDimitry Andric     Result += '}';
9830b57cec5SDimitry Andric     addInt(mapIndex(P->Index));
9840b57cec5SDimitry Andric   }
9850b57cec5SDimitry Andric 
9860b57cec5SDimitry Andric   void VisitDiff(DiffPiece *P) {
9870b57cec5SDimitry Andric     Result += "%diff{";
988fe6060f1SDimitry Andric     Visit(P->Parts[0]);
989fe6060f1SDimitry Andric     Result += "$";
990fe6060f1SDimitry Andric     Visit(P->Parts[1]);
991fe6060f1SDimitry Andric     Result += "$";
992fe6060f1SDimitry Andric     Visit(P->Parts[2]);
9930b57cec5SDimitry Andric     Result += "|";
994fe6060f1SDimitry Andric     Visit(P->Parts[3]);
9950b57cec5SDimitry Andric     Result += "}";
9960b57cec5SDimitry Andric     addInt(mapIndex(P->Indexes[0]));
9970b57cec5SDimitry Andric     Result += ",";
9980b57cec5SDimitry Andric     addInt(mapIndex(P->Indexes[1]));
9990b57cec5SDimitry Andric   }
10000b57cec5SDimitry Andric 
10010b57cec5SDimitry Andric   void addInt(int Val) { Result += std::to_string(Val); }
10020b57cec5SDimitry Andric 
10030b57cec5SDimitry Andric   std::string &Result;
10040b57cec5SDimitry Andric };
10050b57cec5SDimitry Andric 
10060b57cec5SDimitry Andric int DiagnosticTextBuilder::DiagText::parseModifier(StringRef &Text) const {
10070b57cec5SDimitry Andric   if (Text.empty() || !isdigit(Text[0]))
10080b57cec5SDimitry Andric     Builder.PrintFatalError("expected modifier in diagnostic");
10090b57cec5SDimitry Andric   int Val = 0;
10100b57cec5SDimitry Andric   do {
10110b57cec5SDimitry Andric     Val *= 10;
10120b57cec5SDimitry Andric     Val += Text[0] - '0';
10130b57cec5SDimitry Andric     Text = Text.drop_front();
10140b57cec5SDimitry Andric   } while (!Text.empty() && isdigit(Text[0]));
10150b57cec5SDimitry Andric   return Val;
10160b57cec5SDimitry Andric }
10170b57cec5SDimitry Andric 
10180b57cec5SDimitry Andric Piece *DiagnosticTextBuilder::DiagText::parseDiagText(StringRef &Text,
1019fe6060f1SDimitry Andric                                                       StopAt Stop) {
10200b57cec5SDimitry Andric   std::vector<Piece *> Parsed;
10210b57cec5SDimitry Andric 
1022fe6060f1SDimitry Andric   constexpr llvm::StringLiteral StopSets[] = {"%", "%|}", "%|}$"};
1023fe6060f1SDimitry Andric   llvm::StringRef StopSet = StopSets[static_cast<int>(Stop)];
1024fe6060f1SDimitry Andric 
10250b57cec5SDimitry Andric   while (!Text.empty()) {
10260b57cec5SDimitry Andric     size_t End = (size_t)-2;
10270b57cec5SDimitry Andric     do
1028fe6060f1SDimitry Andric       End = Text.find_first_of(StopSet, End + 2);
1029fe6060f1SDimitry Andric     while (
1030fe6060f1SDimitry Andric         End < Text.size() - 1 && Text[End] == '%' &&
1031fe6060f1SDimitry Andric         (Text[End + 1] == '%' || Text[End + 1] == '|' || Text[End + 1] == '$'));
10320b57cec5SDimitry Andric 
10330b57cec5SDimitry Andric     if (End) {
10340b57cec5SDimitry Andric       Parsed.push_back(New<TextPiece>(Text.slice(0, End), "diagtext"));
10350b57cec5SDimitry Andric       Text = Text.slice(End, StringRef::npos);
10360b57cec5SDimitry Andric       if (Text.empty())
10370b57cec5SDimitry Andric         break;
10380b57cec5SDimitry Andric     }
10390b57cec5SDimitry Andric 
1040fe6060f1SDimitry Andric     if (Text[0] == '|' || Text[0] == '}' || Text[0] == '$')
10410b57cec5SDimitry Andric       break;
10420b57cec5SDimitry Andric 
10430b57cec5SDimitry Andric     // Drop the '%'.
10440b57cec5SDimitry Andric     Text = Text.drop_front();
10450b57cec5SDimitry Andric 
10460b57cec5SDimitry Andric     // Extract the (optional) modifier.
10470b57cec5SDimitry Andric     size_t ModLength = Text.find_first_of("0123456789{");
10480b57cec5SDimitry Andric     StringRef Modifier = Text.slice(0, ModLength);
10490b57cec5SDimitry Andric     Text = Text.slice(ModLength, StringRef::npos);
10500b57cec5SDimitry Andric     ModifierType ModType = llvm::StringSwitch<ModifierType>{Modifier}
10510b57cec5SDimitry Andric                                .Case("select", MT_Select)
10520b57cec5SDimitry Andric                                .Case("sub", MT_Sub)
10530b57cec5SDimitry Andric                                .Case("diff", MT_Diff)
10540b57cec5SDimitry Andric                                .Case("plural", MT_Plural)
10550b57cec5SDimitry Andric                                .Case("s", MT_S)
10560b57cec5SDimitry Andric                                .Case("ordinal", MT_Ordinal)
10570b57cec5SDimitry Andric                                .Case("q", MT_Q)
10580b57cec5SDimitry Andric                                .Case("objcclass", MT_ObjCClass)
10590b57cec5SDimitry Andric                                .Case("objcinstance", MT_ObjCInstance)
10600b57cec5SDimitry Andric                                .Case("", MT_Placeholder)
10610b57cec5SDimitry Andric                                .Default(MT_Unknown);
10620b57cec5SDimitry Andric 
1063fe6060f1SDimitry Andric     auto ExpectAndConsume = [&](StringRef Prefix) {
1064fe6060f1SDimitry Andric       if (!Text.consume_front(Prefix))
1065fe6060f1SDimitry Andric         Builder.PrintFatalError("expected '" + Prefix + "' while parsing %" +
1066fe6060f1SDimitry Andric                                 Modifier);
1067fe6060f1SDimitry Andric     };
1068fe6060f1SDimitry Andric 
10690b57cec5SDimitry Andric     switch (ModType) {
10700b57cec5SDimitry Andric     case MT_Unknown:
10710b57cec5SDimitry Andric       Builder.PrintFatalError("Unknown modifier type: " + Modifier);
10720b57cec5SDimitry Andric     case MT_Select: {
10730b57cec5SDimitry Andric       SelectPiece *Select = New<SelectPiece>(MT_Select);
10740b57cec5SDimitry Andric       do {
10750b57cec5SDimitry Andric         Text = Text.drop_front(); // '{' or '|'
1076fe6060f1SDimitry Andric         Select->Options.push_back(
1077fe6060f1SDimitry Andric             parseDiagText(Text, StopAt::PipeOrCloseBrace));
10780b57cec5SDimitry Andric         assert(!Text.empty() && "malformed %select");
10790b57cec5SDimitry Andric       } while (Text.front() == '|');
1080fe6060f1SDimitry Andric       ExpectAndConsume("}");
10810b57cec5SDimitry Andric       Select->Index = parseModifier(Text);
10820b57cec5SDimitry Andric       Parsed.push_back(Select);
10830b57cec5SDimitry Andric       continue;
10840b57cec5SDimitry Andric     }
10850b57cec5SDimitry Andric     case MT_Plural: {
10860b57cec5SDimitry Andric       PluralPiece *Plural = New<PluralPiece>();
10870b57cec5SDimitry Andric       do {
10880b57cec5SDimitry Andric         Text = Text.drop_front(); // '{' or '|'
1089*0fca6ea1SDimitry Andric         size_t End = Text.find_first_of(':');
10900b57cec5SDimitry Andric         if (End == StringRef::npos)
10910b57cec5SDimitry Andric           Builder.PrintFatalError("expected ':' while parsing %plural");
10920b57cec5SDimitry Andric         ++End;
10930b57cec5SDimitry Andric         assert(!Text.empty());
10940b57cec5SDimitry Andric         Plural->OptionPrefixes.push_back(
10950b57cec5SDimitry Andric             New<TextPiece>(Text.slice(0, End), "diagtext"));
10960b57cec5SDimitry Andric         Text = Text.slice(End, StringRef::npos);
1097fe6060f1SDimitry Andric         Plural->Options.push_back(
1098fe6060f1SDimitry Andric             parseDiagText(Text, StopAt::PipeOrCloseBrace));
1099fe6060f1SDimitry Andric         assert(!Text.empty() && "malformed %plural");
11000b57cec5SDimitry Andric       } while (Text.front() == '|');
1101fe6060f1SDimitry Andric       ExpectAndConsume("}");
11020b57cec5SDimitry Andric       Plural->Index = parseModifier(Text);
11030b57cec5SDimitry Andric       Parsed.push_back(Plural);
11040b57cec5SDimitry Andric       continue;
11050b57cec5SDimitry Andric     }
11060b57cec5SDimitry Andric     case MT_Sub: {
11070b57cec5SDimitry Andric       SubstitutionPiece *Sub = New<SubstitutionPiece>();
1108fe6060f1SDimitry Andric       ExpectAndConsume("{");
11090b57cec5SDimitry Andric       size_t NameSize = Text.find_first_of('}');
11100b57cec5SDimitry Andric       assert(NameSize != size_t(-1) && "failed to find the end of the name");
11110b57cec5SDimitry Andric       assert(NameSize != 0 && "empty name?");
11120b57cec5SDimitry Andric       Sub->Name = Text.substr(0, NameSize).str();
11130b57cec5SDimitry Andric       Text = Text.drop_front(NameSize);
1114fe6060f1SDimitry Andric       ExpectAndConsume("}");
11150b57cec5SDimitry Andric       if (!Text.empty()) {
11160b57cec5SDimitry Andric         while (true) {
11170b57cec5SDimitry Andric           if (!isdigit(Text[0]))
11180b57cec5SDimitry Andric             break;
11190b57cec5SDimitry Andric           Sub->Modifiers.push_back(parseModifier(Text));
1120*0fca6ea1SDimitry Andric           if (!Text.consume_front(","))
11210b57cec5SDimitry Andric             break;
11220b57cec5SDimitry Andric           assert(!Text.empty() && isdigit(Text[0]) &&
11230b57cec5SDimitry Andric                  "expected another modifier");
11240b57cec5SDimitry Andric         }
11250b57cec5SDimitry Andric       }
11260b57cec5SDimitry Andric       Parsed.push_back(Sub);
11270b57cec5SDimitry Andric       continue;
11280b57cec5SDimitry Andric     }
11290b57cec5SDimitry Andric     case MT_Diff: {
11300b57cec5SDimitry Andric       DiffPiece *Diff = New<DiffPiece>();
1131fe6060f1SDimitry Andric       ExpectAndConsume("{");
1132fe6060f1SDimitry Andric       Diff->Parts[0] = parseDiagText(Text, StopAt::Dollar);
1133fe6060f1SDimitry Andric       ExpectAndConsume("$");
1134fe6060f1SDimitry Andric       Diff->Parts[1] = parseDiagText(Text, StopAt::Dollar);
1135fe6060f1SDimitry Andric       ExpectAndConsume("$");
1136fe6060f1SDimitry Andric       Diff->Parts[2] = parseDiagText(Text, StopAt::PipeOrCloseBrace);
1137fe6060f1SDimitry Andric       ExpectAndConsume("|");
1138fe6060f1SDimitry Andric       Diff->Parts[3] = parseDiagText(Text, StopAt::PipeOrCloseBrace);
1139fe6060f1SDimitry Andric       ExpectAndConsume("}");
11400b57cec5SDimitry Andric       Diff->Indexes[0] = parseModifier(Text);
1141fe6060f1SDimitry Andric       ExpectAndConsume(",");
11420b57cec5SDimitry Andric       Diff->Indexes[1] = parseModifier(Text);
11430b57cec5SDimitry Andric       Parsed.push_back(Diff);
11440b57cec5SDimitry Andric       continue;
11450b57cec5SDimitry Andric     }
11460b57cec5SDimitry Andric     case MT_S: {
11470b57cec5SDimitry Andric       SelectPiece *Select = New<SelectPiece>(ModType);
11480b57cec5SDimitry Andric       Select->Options.push_back(New<TextPiece>(""));
11490b57cec5SDimitry Andric       Select->Options.push_back(New<TextPiece>("s", "diagtext"));
11500b57cec5SDimitry Andric       Select->Index = parseModifier(Text);
11510b57cec5SDimitry Andric       Parsed.push_back(Select);
11520b57cec5SDimitry Andric       continue;
11530b57cec5SDimitry Andric     }
11540b57cec5SDimitry Andric     case MT_Q:
11550b57cec5SDimitry Andric     case MT_Placeholder:
11560b57cec5SDimitry Andric     case MT_ObjCClass:
11570b57cec5SDimitry Andric     case MT_ObjCInstance:
11580b57cec5SDimitry Andric     case MT_Ordinal: {
11590b57cec5SDimitry Andric       Parsed.push_back(New<PlaceholderPiece>(ModType, parseModifier(Text)));
11600b57cec5SDimitry Andric       continue;
11610b57cec5SDimitry Andric     }
11620b57cec5SDimitry Andric     }
11630b57cec5SDimitry Andric   }
11640b57cec5SDimitry Andric 
11650b57cec5SDimitry Andric   return New<MultiPiece>(Parsed);
11660b57cec5SDimitry Andric }
11670b57cec5SDimitry Andric 
11680b57cec5SDimitry Andric std::vector<std::string>
11690b57cec5SDimitry Andric DiagnosticTextBuilder::buildForDocumentation(StringRef Severity,
11700b57cec5SDimitry Andric                                              const Record *R) {
11710b57cec5SDimitry Andric   EvaluatingRecordGuard Guard(&EvaluatingRecord, R);
1172bdd1243dSDimitry Andric   StringRef Text = R->getValueAsString("Summary");
11730b57cec5SDimitry Andric 
11740b57cec5SDimitry Andric   DiagText D(*this, Text);
11750b57cec5SDimitry Andric   TextPiece *Prefix = D.New<TextPiece>(Severity, Severity);
11760b57cec5SDimitry Andric   Prefix->Text += ": ";
11770b57cec5SDimitry Andric   auto *MP = dyn_cast<MultiPiece>(D.Root);
11780b57cec5SDimitry Andric   if (!MP) {
11790b57cec5SDimitry Andric     MP = D.New<MultiPiece>();
11800b57cec5SDimitry Andric     MP->Pieces.push_back(D.Root);
11810b57cec5SDimitry Andric     D.Root = MP;
11820b57cec5SDimitry Andric   }
11830b57cec5SDimitry Andric   MP->Pieces.insert(MP->Pieces.begin(), Prefix);
11840b57cec5SDimitry Andric   std::vector<std::string> Result;
11850b57cec5SDimitry Andric   DiagTextDocPrinter{*this, Result}.Visit(D.Root);
11860b57cec5SDimitry Andric   return Result;
11870b57cec5SDimitry Andric }
11880b57cec5SDimitry Andric 
11890b57cec5SDimitry Andric std::string DiagnosticTextBuilder::buildForDefinition(const Record *R) {
11900b57cec5SDimitry Andric   EvaluatingRecordGuard Guard(&EvaluatingRecord, R);
1191bdd1243dSDimitry Andric   StringRef Text = R->getValueAsString("Summary");
11920b57cec5SDimitry Andric   DiagText D(*this, Text);
11930b57cec5SDimitry Andric   std::string Result;
11940b57cec5SDimitry Andric   DiagTextPrinter{*this, Result}.Visit(D.Root);
11950b57cec5SDimitry Andric   return Result;
11960b57cec5SDimitry Andric }
11970b57cec5SDimitry Andric 
11980b57cec5SDimitry Andric } // namespace
11990b57cec5SDimitry Andric 
12000b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
12010b57cec5SDimitry Andric // Warning Tables (.inc file) generation.
12020b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
12030b57cec5SDimitry Andric 
12040b57cec5SDimitry Andric static bool isError(const Record &Diag) {
12055ffd83dbSDimitry Andric   const std::string &ClsName =
12065ffd83dbSDimitry Andric       std::string(Diag.getValueAsDef("Class")->getName());
12070b57cec5SDimitry Andric   return ClsName == "CLASS_ERROR";
12080b57cec5SDimitry Andric }
12090b57cec5SDimitry Andric 
12100b57cec5SDimitry Andric static bool isRemark(const Record &Diag) {
12115ffd83dbSDimitry Andric   const std::string &ClsName =
12125ffd83dbSDimitry Andric       std::string(Diag.getValueAsDef("Class")->getName());
12130b57cec5SDimitry Andric   return ClsName == "CLASS_REMARK";
12140b57cec5SDimitry Andric }
12150b57cec5SDimitry Andric 
1216*0fca6ea1SDimitry Andric // Presumes the text has been split at the first whitespace or hyphen.
1217*0fca6ea1SDimitry Andric static bool isExemptAtStart(StringRef Text) {
1218*0fca6ea1SDimitry Andric   // Fast path, the first character is lowercase or not alphanumeric.
1219*0fca6ea1SDimitry Andric   if (Text.empty() || isLower(Text[0]) || !isAlnum(Text[0]))
1220*0fca6ea1SDimitry Andric     return true;
1221*0fca6ea1SDimitry Andric 
1222*0fca6ea1SDimitry Andric   // If the text is all uppercase (or numbers, +, or _), then we assume it's an
1223*0fca6ea1SDimitry Andric   // acronym and that's allowed. This covers cases like ISO, C23, C++14, and
1224*0fca6ea1SDimitry Andric   // OBJECT_MODE. However, if there's only a single letter other than "C", we
1225*0fca6ea1SDimitry Andric   // do not exempt it so that we catch a case like "A really bad idea" while
1226*0fca6ea1SDimitry Andric   // still allowing a case like "C does not allow...".
1227*0fca6ea1SDimitry Andric   if (llvm::all_of(Text, [](char C) {
1228*0fca6ea1SDimitry Andric         return isUpper(C) || isDigit(C) || C == '+' || C == '_';
1229*0fca6ea1SDimitry Andric       }))
1230*0fca6ea1SDimitry Andric     return Text.size() > 1 || Text[0] == 'C';
1231*0fca6ea1SDimitry Andric 
1232*0fca6ea1SDimitry Andric   // Otherwise, there are a few other exemptions.
1233*0fca6ea1SDimitry Andric   return StringSwitch<bool>(Text)
1234*0fca6ea1SDimitry Andric       .Case("AddressSanitizer", true)
1235*0fca6ea1SDimitry Andric       .Case("CFString", true)
1236*0fca6ea1SDimitry Andric       .Case("Clang", true)
1237*0fca6ea1SDimitry Andric       .Case("Fuchsia", true)
1238*0fca6ea1SDimitry Andric       .Case("GNUstep", true)
1239*0fca6ea1SDimitry Andric       .Case("IBOutletCollection", true)
1240*0fca6ea1SDimitry Andric       .Case("Microsoft", true)
1241*0fca6ea1SDimitry Andric       .Case("Neon", true)
1242*0fca6ea1SDimitry Andric       .StartsWith("NSInvocation", true) // NSInvocation, NSInvocation's
1243*0fca6ea1SDimitry Andric       .Case("Objective", true) // Objective-C (hyphen is a word boundary)
1244*0fca6ea1SDimitry Andric       .Case("OpenACC", true)
1245*0fca6ea1SDimitry Andric       .Case("OpenCL", true)
1246*0fca6ea1SDimitry Andric       .Case("OpenMP", true)
1247*0fca6ea1SDimitry Andric       .Case("Pascal", true)
1248*0fca6ea1SDimitry Andric       .Case("Swift", true)
1249*0fca6ea1SDimitry Andric       .Case("Unicode", true)
1250*0fca6ea1SDimitry Andric       .Case("Vulkan", true)
1251*0fca6ea1SDimitry Andric       .Case("WebAssembly", true)
1252*0fca6ea1SDimitry Andric       .Default(false);
1253*0fca6ea1SDimitry Andric }
1254*0fca6ea1SDimitry Andric 
1255*0fca6ea1SDimitry Andric // Does not presume the text has been split at all.
1256*0fca6ea1SDimitry Andric static bool isExemptAtEnd(StringRef Text) {
1257*0fca6ea1SDimitry Andric   // Rather than come up with a list of characters that are allowed, we go the
1258*0fca6ea1SDimitry Andric   // other way and look only for characters that are not allowed.
1259*0fca6ea1SDimitry Andric   switch (Text.back()) {
1260*0fca6ea1SDimitry Andric   default:
1261*0fca6ea1SDimitry Andric     return true;
1262*0fca6ea1SDimitry Andric   case '?':
1263*0fca6ea1SDimitry Andric     // Explicitly allowed to support "; did you mean?".
1264*0fca6ea1SDimitry Andric     return true;
1265*0fca6ea1SDimitry Andric   case '.':
1266*0fca6ea1SDimitry Andric   case '!':
1267*0fca6ea1SDimitry Andric     return false;
1268*0fca6ea1SDimitry Andric   }
1269*0fca6ea1SDimitry Andric }
1270*0fca6ea1SDimitry Andric 
1271*0fca6ea1SDimitry Andric static void verifyDiagnosticWording(const Record &Diag) {
1272*0fca6ea1SDimitry Andric   StringRef FullDiagText = Diag.getValueAsString("Summary");
1273*0fca6ea1SDimitry Andric 
1274*0fca6ea1SDimitry Andric   auto DiagnoseStart = [&](StringRef Text) {
1275*0fca6ea1SDimitry Andric     // Verify that the text does not start with a capital letter, except for
1276*0fca6ea1SDimitry Andric     // special cases that are exempt like ISO and C++. Find the first word
1277*0fca6ea1SDimitry Andric     // by looking for a word breaking character.
1278*0fca6ea1SDimitry Andric     char Separators[] = {' ', '-', ',', '}'};
1279*0fca6ea1SDimitry Andric     auto Iter = std::find_first_of(
1280*0fca6ea1SDimitry Andric         Text.begin(), Text.end(), std::begin(Separators), std::end(Separators));
1281*0fca6ea1SDimitry Andric 
1282*0fca6ea1SDimitry Andric     StringRef First = Text.substr(0, Iter - Text.begin());
1283*0fca6ea1SDimitry Andric     if (!isExemptAtStart(First)) {
1284*0fca6ea1SDimitry Andric       PrintError(&Diag,
1285*0fca6ea1SDimitry Andric                  "Diagnostics should not start with a capital letter; '" +
1286*0fca6ea1SDimitry Andric                      First + "' is invalid");
1287*0fca6ea1SDimitry Andric     }
1288*0fca6ea1SDimitry Andric   };
1289*0fca6ea1SDimitry Andric 
1290*0fca6ea1SDimitry Andric   auto DiagnoseEnd = [&](StringRef Text) {
1291*0fca6ea1SDimitry Andric     // Verify that the text does not end with punctuation like '.' or '!'.
1292*0fca6ea1SDimitry Andric     if (!isExemptAtEnd(Text)) {
1293*0fca6ea1SDimitry Andric       PrintError(&Diag, "Diagnostics should not end with punctuation; '" +
1294*0fca6ea1SDimitry Andric                             Text.substr(Text.size() - 1, 1) + "' is invalid");
1295*0fca6ea1SDimitry Andric     }
1296*0fca6ea1SDimitry Andric   };
1297*0fca6ea1SDimitry Andric 
1298*0fca6ea1SDimitry Andric   // If the diagnostic starts with %select, look through it to see whether any
1299*0fca6ea1SDimitry Andric   // of the options will cause a problem.
1300*0fca6ea1SDimitry Andric   if (FullDiagText.starts_with("%select{")) {
1301*0fca6ea1SDimitry Andric     // Do a balanced delimiter scan from the start of the text to find the
1302*0fca6ea1SDimitry Andric     // closing '}', skipping intermediary {} pairs.
1303*0fca6ea1SDimitry Andric 
1304*0fca6ea1SDimitry Andric     size_t BraceCount = 1;
1305*0fca6ea1SDimitry Andric     constexpr size_t PercentSelectBraceLen = sizeof("%select{") - 1;
1306*0fca6ea1SDimitry Andric     auto Iter = FullDiagText.begin() + PercentSelectBraceLen;
1307*0fca6ea1SDimitry Andric     for (auto End = FullDiagText.end(); Iter != End; ++Iter) {
1308*0fca6ea1SDimitry Andric       char Ch = *Iter;
1309*0fca6ea1SDimitry Andric       if (Ch == '{')
1310*0fca6ea1SDimitry Andric         ++BraceCount;
1311*0fca6ea1SDimitry Andric       else if (Ch == '}')
1312*0fca6ea1SDimitry Andric         --BraceCount;
1313*0fca6ea1SDimitry Andric       if (!BraceCount)
1314*0fca6ea1SDimitry Andric         break;
1315*0fca6ea1SDimitry Andric     }
1316*0fca6ea1SDimitry Andric     // Defending against a malformed diagnostic string.
1317*0fca6ea1SDimitry Andric     if (BraceCount != 0)
1318*0fca6ea1SDimitry Andric       return;
1319*0fca6ea1SDimitry Andric 
1320*0fca6ea1SDimitry Andric     StringRef SelectText =
1321*0fca6ea1SDimitry Andric         FullDiagText.substr(PercentSelectBraceLen, Iter - FullDiagText.begin() -
1322*0fca6ea1SDimitry Andric                                                        PercentSelectBraceLen);
1323*0fca6ea1SDimitry Andric     SmallVector<StringRef, 4> SelectPieces;
1324*0fca6ea1SDimitry Andric     SelectText.split(SelectPieces, '|');
1325*0fca6ea1SDimitry Andric 
1326*0fca6ea1SDimitry Andric     // Walk over all of the individual pieces of select text to see if any of
1327*0fca6ea1SDimitry Andric     // them start with an invalid character. If any of the select pieces is
1328*0fca6ea1SDimitry Andric     // empty, we need to look at the first word after the %select to see
1329*0fca6ea1SDimitry Andric     // whether that is invalid or not. If all of the pieces are fine, then we
1330*0fca6ea1SDimitry Andric     // don't need to check anything else about the start of the diagnostic.
1331*0fca6ea1SDimitry Andric     bool CheckSecondWord = false;
1332*0fca6ea1SDimitry Andric     for (StringRef Piece : SelectPieces) {
1333*0fca6ea1SDimitry Andric       if (Piece.empty())
1334*0fca6ea1SDimitry Andric         CheckSecondWord = true;
1335*0fca6ea1SDimitry Andric       else
1336*0fca6ea1SDimitry Andric         DiagnoseStart(Piece);
1337*0fca6ea1SDimitry Andric     }
1338*0fca6ea1SDimitry Andric 
1339*0fca6ea1SDimitry Andric     if (CheckSecondWord) {
1340*0fca6ea1SDimitry Andric       // There was an empty select piece, so we need to check the second
1341*0fca6ea1SDimitry Andric       // word. This catches situations like '%select{|fine}0 Not okay'. Add
1342*0fca6ea1SDimitry Andric       // two to account for the closing curly brace and the number after it.
1343*0fca6ea1SDimitry Andric       StringRef AfterSelect =
1344*0fca6ea1SDimitry Andric           FullDiagText.substr(Iter - FullDiagText.begin() + 2).ltrim();
1345*0fca6ea1SDimitry Andric       DiagnoseStart(AfterSelect);
1346*0fca6ea1SDimitry Andric     }
1347*0fca6ea1SDimitry Andric   } else {
1348*0fca6ea1SDimitry Andric     // If the start of the diagnostic is not %select, we can check the first
1349*0fca6ea1SDimitry Andric     // word and be done with it.
1350*0fca6ea1SDimitry Andric     DiagnoseStart(FullDiagText);
1351*0fca6ea1SDimitry Andric   }
1352*0fca6ea1SDimitry Andric 
1353*0fca6ea1SDimitry Andric   // If the last character in the diagnostic is a number preceded by a }, scan
1354*0fca6ea1SDimitry Andric   // backwards to see if this is for a %select{...}0. If it is, we need to look
1355*0fca6ea1SDimitry Andric   // at each piece to see whether it ends in punctuation or not.
1356*0fca6ea1SDimitry Andric   bool StillNeedToDiagEnd = true;
1357*0fca6ea1SDimitry Andric   if (isDigit(FullDiagText.back()) && *(FullDiagText.end() - 2) == '}') {
1358*0fca6ea1SDimitry Andric     // Scan backwards to find the opening curly brace.
1359*0fca6ea1SDimitry Andric     size_t BraceCount = 1;
1360*0fca6ea1SDimitry Andric     auto Iter = FullDiagText.end() - sizeof("}0");
1361*0fca6ea1SDimitry Andric     for (auto End = FullDiagText.begin(); Iter != End; --Iter) {
1362*0fca6ea1SDimitry Andric       char Ch = *Iter;
1363*0fca6ea1SDimitry Andric       if (Ch == '}')
1364*0fca6ea1SDimitry Andric         ++BraceCount;
1365*0fca6ea1SDimitry Andric       else if (Ch == '{')
1366*0fca6ea1SDimitry Andric         --BraceCount;
1367*0fca6ea1SDimitry Andric       if (!BraceCount)
1368*0fca6ea1SDimitry Andric         break;
1369*0fca6ea1SDimitry Andric     }
1370*0fca6ea1SDimitry Andric     // Defending against a malformed diagnostic string.
1371*0fca6ea1SDimitry Andric     if (BraceCount != 0)
1372*0fca6ea1SDimitry Andric       return;
1373*0fca6ea1SDimitry Andric 
1374*0fca6ea1SDimitry Andric     // Continue the backwards scan to find the word before the '{' to see if it
1375*0fca6ea1SDimitry Andric     // is 'select'.
1376*0fca6ea1SDimitry Andric     constexpr size_t SelectLen = sizeof("select") - 1;
1377*0fca6ea1SDimitry Andric     bool IsSelect =
1378*0fca6ea1SDimitry Andric         (FullDiagText.substr(Iter - SelectLen - FullDiagText.begin(),
1379*0fca6ea1SDimitry Andric                              SelectLen) == "select");
1380*0fca6ea1SDimitry Andric     if (IsSelect) {
1381*0fca6ea1SDimitry Andric       // Gather the content between the {} for the select in question so we can
1382*0fca6ea1SDimitry Andric       // split it into pieces.
1383*0fca6ea1SDimitry Andric       StillNeedToDiagEnd = false; // No longer need to handle the end.
1384*0fca6ea1SDimitry Andric       StringRef SelectText =
1385*0fca6ea1SDimitry Andric           FullDiagText.substr(Iter - FullDiagText.begin() + /*{*/ 1,
1386*0fca6ea1SDimitry Andric                               FullDiagText.end() - Iter - /*pos before }0*/ 3);
1387*0fca6ea1SDimitry Andric       SmallVector<StringRef, 4> SelectPieces;
1388*0fca6ea1SDimitry Andric       SelectText.split(SelectPieces, '|');
1389*0fca6ea1SDimitry Andric       for (StringRef Piece : SelectPieces) {
1390*0fca6ea1SDimitry Andric         // Not worrying about a situation like: "this is bar. %select{foo|}0".
1391*0fca6ea1SDimitry Andric         if (!Piece.empty())
1392*0fca6ea1SDimitry Andric           DiagnoseEnd(Piece);
1393*0fca6ea1SDimitry Andric       }
1394*0fca6ea1SDimitry Andric     }
1395*0fca6ea1SDimitry Andric   }
1396*0fca6ea1SDimitry Andric 
1397*0fca6ea1SDimitry Andric   // If we didn't already cover the diagnostic because of a %select, handle it
1398*0fca6ea1SDimitry Andric   // now.
1399*0fca6ea1SDimitry Andric   if (StillNeedToDiagEnd)
1400*0fca6ea1SDimitry Andric     DiagnoseEnd(FullDiagText);
1401*0fca6ea1SDimitry Andric 
1402*0fca6ea1SDimitry Andric   // FIXME: This could also be improved by looking for instances of clang or
1403*0fca6ea1SDimitry Andric   // gcc in the diagnostic and recommend Clang or GCC instead. However, this
1404*0fca6ea1SDimitry Andric   // runs into odd situations like [[clang::warn_unused_result]],
1405*0fca6ea1SDimitry Andric   // #pragma clang, or --unwindlib=libgcc.
1406*0fca6ea1SDimitry Andric }
14070b57cec5SDimitry Andric 
14080b57cec5SDimitry Andric /// ClangDiagsDefsEmitter - The top-level class emits .def files containing
14090b57cec5SDimitry Andric /// declarations of Clang diagnostics.
1410a7dea167SDimitry Andric void clang::EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS,
14110b57cec5SDimitry Andric                                const std::string &Component) {
14120b57cec5SDimitry Andric   // Write the #if guard
14130b57cec5SDimitry Andric   if (!Component.empty()) {
14140b57cec5SDimitry Andric     std::string ComponentName = StringRef(Component).upper();
14150b57cec5SDimitry Andric     OS << "#ifdef " << ComponentName << "START\n";
14160b57cec5SDimitry Andric     OS << "__" << ComponentName << "START = DIAG_START_" << ComponentName
14170b57cec5SDimitry Andric        << ",\n";
14180b57cec5SDimitry Andric     OS << "#undef " << ComponentName << "START\n";
14190b57cec5SDimitry Andric     OS << "#endif\n\n";
14200b57cec5SDimitry Andric   }
14210b57cec5SDimitry Andric 
14220b57cec5SDimitry Andric   DiagnosticTextBuilder DiagTextBuilder(Records);
14230b57cec5SDimitry Andric 
14240b57cec5SDimitry Andric   std::vector<Record *> Diags = Records.getAllDerivedDefinitions("Diagnostic");
14250b57cec5SDimitry Andric 
14260b57cec5SDimitry Andric   std::vector<Record*> DiagGroups
14270b57cec5SDimitry Andric     = Records.getAllDerivedDefinitions("DiagGroup");
14280b57cec5SDimitry Andric 
14290b57cec5SDimitry Andric   std::map<std::string, GroupInfo> DiagsInGroup;
14300b57cec5SDimitry Andric   groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
14310b57cec5SDimitry Andric 
14320b57cec5SDimitry Andric   DiagCategoryIDMap CategoryIDs(Records);
14330b57cec5SDimitry Andric   DiagGroupParentMap DGParentMap(Records);
14340b57cec5SDimitry Andric 
14350b57cec5SDimitry Andric   // Compute the set of diagnostics that are in -Wpedantic.
14360b57cec5SDimitry Andric   RecordSet DiagsInPedantic;
14370b57cec5SDimitry Andric   InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
14380b57cec5SDimitry Andric   inferPedantic.compute(&DiagsInPedantic, (RecordVec*)nullptr);
14390b57cec5SDimitry Andric 
14400b57cec5SDimitry Andric   for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
14410b57cec5SDimitry Andric     const Record &R = *Diags[i];
14420b57cec5SDimitry Andric 
14430b57cec5SDimitry Andric     // Check if this is an error that is accidentally in a warning
14440b57cec5SDimitry Andric     // group.
14450b57cec5SDimitry Andric     if (isError(R)) {
14460b57cec5SDimitry Andric       if (DefInit *Group = dyn_cast<DefInit>(R.getValueInit("Group"))) {
14470b57cec5SDimitry Andric         const Record *GroupRec = Group->getDef();
14485ffd83dbSDimitry Andric         const std::string &GroupName =
14495ffd83dbSDimitry Andric             std::string(GroupRec->getValueAsString("GroupName"));
14500b57cec5SDimitry Andric         PrintFatalError(R.getLoc(), "Error " + R.getName() +
14510b57cec5SDimitry Andric                       " cannot be in a warning group [" + GroupName + "]");
14520b57cec5SDimitry Andric       }
14530b57cec5SDimitry Andric     }
14540b57cec5SDimitry Andric 
14550b57cec5SDimitry Andric     // Check that all remarks have an associated diagnostic group.
14560b57cec5SDimitry Andric     if (isRemark(R)) {
14570b57cec5SDimitry Andric       if (!isa<DefInit>(R.getValueInit("Group"))) {
14580b57cec5SDimitry Andric         PrintFatalError(R.getLoc(), "Error " + R.getName() +
14590b57cec5SDimitry Andric                                         " not in any diagnostic group");
14600b57cec5SDimitry Andric       }
14610b57cec5SDimitry Andric     }
14620b57cec5SDimitry Andric 
14630b57cec5SDimitry Andric     // Filter by component.
14640b57cec5SDimitry Andric     if (!Component.empty() && Component != R.getValueAsString("Component"))
14650b57cec5SDimitry Andric       continue;
14660b57cec5SDimitry Andric 
1467*0fca6ea1SDimitry Andric     // Validate diagnostic wording for common issues.
1468*0fca6ea1SDimitry Andric     verifyDiagnosticWording(R);
1469*0fca6ea1SDimitry Andric 
14700b57cec5SDimitry Andric     OS << "DIAG(" << R.getName() << ", ";
14710b57cec5SDimitry Andric     OS << R.getValueAsDef("Class")->getName();
14720b57cec5SDimitry Andric     OS << ", (unsigned)diag::Severity::"
14730b57cec5SDimitry Andric        << R.getValueAsDef("DefaultSeverity")->getValueAsString("Name");
14740b57cec5SDimitry Andric 
14750b57cec5SDimitry Andric     // Description string.
14760b57cec5SDimitry Andric     OS << ", \"";
14770b57cec5SDimitry Andric     OS.write_escaped(DiagTextBuilder.buildForDefinition(&R)) << '"';
14780b57cec5SDimitry Andric 
1479349cc55cSDimitry Andric     // Warning group associated with the diagnostic. This is stored as an index
1480349cc55cSDimitry Andric     // into the alphabetically sorted warning group table.
14810b57cec5SDimitry Andric     if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) {
14825ffd83dbSDimitry Andric       std::map<std::string, GroupInfo>::iterator I = DiagsInGroup.find(
14835ffd83dbSDimitry Andric           std::string(DI->getDef()->getValueAsString("GroupName")));
14840b57cec5SDimitry Andric       assert(I != DiagsInGroup.end());
14850b57cec5SDimitry Andric       OS << ", " << I->second.IDNo;
14860b57cec5SDimitry Andric     } else if (DiagsInPedantic.count(&R)) {
14870b57cec5SDimitry Andric       std::map<std::string, GroupInfo>::iterator I =
14880b57cec5SDimitry Andric         DiagsInGroup.find("pedantic");
14890b57cec5SDimitry Andric       assert(I != DiagsInGroup.end() && "pedantic group not defined");
14900b57cec5SDimitry Andric       OS << ", " << I->second.IDNo;
14910b57cec5SDimitry Andric     } else {
14920b57cec5SDimitry Andric       OS << ", 0";
14930b57cec5SDimitry Andric     }
14940b57cec5SDimitry Andric 
14950b57cec5SDimitry Andric     // SFINAE response.
14960b57cec5SDimitry Andric     OS << ", " << R.getValueAsDef("SFINAE")->getName();
14970b57cec5SDimitry Andric 
14980b57cec5SDimitry Andric     // Default warning has no Werror bit.
14990b57cec5SDimitry Andric     if (R.getValueAsBit("WarningNoWerror"))
15000b57cec5SDimitry Andric       OS << ", true";
15010b57cec5SDimitry Andric     else
15020b57cec5SDimitry Andric       OS << ", false";
15030b57cec5SDimitry Andric 
15040b57cec5SDimitry Andric     if (R.getValueAsBit("ShowInSystemHeader"))
15050b57cec5SDimitry Andric       OS << ", true";
15060b57cec5SDimitry Andric     else
15070b57cec5SDimitry Andric       OS << ", false";
15080b57cec5SDimitry Andric 
150904eeddc0SDimitry Andric     if (R.getValueAsBit("ShowInSystemMacro"))
151004eeddc0SDimitry Andric       OS << ", true";
151104eeddc0SDimitry Andric     else
151204eeddc0SDimitry Andric       OS << ", false";
151304eeddc0SDimitry Andric 
1514e8d8bef9SDimitry Andric     if (R.getValueAsBit("Deferrable"))
1515e8d8bef9SDimitry Andric       OS << ", true";
1516e8d8bef9SDimitry Andric     else
1517e8d8bef9SDimitry Andric       OS << ", false";
1518e8d8bef9SDimitry Andric 
15190b57cec5SDimitry Andric     // Category number.
15200b57cec5SDimitry Andric     OS << ", " << CategoryIDs.getID(getDiagnosticCategory(&R, DGParentMap));
15210b57cec5SDimitry Andric     OS << ")\n";
15220b57cec5SDimitry Andric   }
15230b57cec5SDimitry Andric }
15240b57cec5SDimitry Andric 
15250b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
15260b57cec5SDimitry Andric // Warning Group Tables generation
15270b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
15280b57cec5SDimitry Andric 
15290b57cec5SDimitry Andric static std::string getDiagCategoryEnum(llvm::StringRef name) {
15300b57cec5SDimitry Andric   if (name.empty())
15310b57cec5SDimitry Andric     return "DiagCat_None";
15320b57cec5SDimitry Andric   SmallString<256> enumName = llvm::StringRef("DiagCat_");
15330b57cec5SDimitry Andric   for (llvm::StringRef::iterator I = name.begin(), E = name.end(); I != E; ++I)
15340b57cec5SDimitry Andric     enumName += isalnum(*I) ? *I : '_';
15357a6dacacSDimitry Andric   return std::string(enumName);
15360b57cec5SDimitry Andric }
15370b57cec5SDimitry Andric 
15380b57cec5SDimitry Andric /// Emit the array of diagnostic subgroups.
15390b57cec5SDimitry Andric ///
15400b57cec5SDimitry Andric /// The array of diagnostic subgroups contains for each group a list of its
15410b57cec5SDimitry Andric /// subgroups. The individual lists are separated by '-1'. Groups with no
15420b57cec5SDimitry Andric /// subgroups are skipped.
15430b57cec5SDimitry Andric ///
15440b57cec5SDimitry Andric /// \code
15450b57cec5SDimitry Andric ///   static const int16_t DiagSubGroups[] = {
15460b57cec5SDimitry Andric ///     /* Empty */ -1,
15470b57cec5SDimitry Andric ///     /* DiagSubGroup0 */ 142, -1,
15480b57cec5SDimitry Andric ///     /* DiagSubGroup13 */ 265, 322, 399, -1
15490b57cec5SDimitry Andric ///   }
15500b57cec5SDimitry Andric /// \endcode
15510b57cec5SDimitry Andric ///
15520b57cec5SDimitry Andric static void emitDiagSubGroups(std::map<std::string, GroupInfo> &DiagsInGroup,
15530b57cec5SDimitry Andric                               RecordVec &GroupsInPedantic, raw_ostream &OS) {
15540b57cec5SDimitry Andric   OS << "static const int16_t DiagSubGroups[] = {\n"
15550b57cec5SDimitry Andric      << "  /* Empty */ -1,\n";
15560b57cec5SDimitry Andric   for (auto const &I : DiagsInGroup) {
15570b57cec5SDimitry Andric     const bool IsPedantic = I.first == "pedantic";
15580b57cec5SDimitry Andric 
15590b57cec5SDimitry Andric     const std::vector<std::string> &SubGroups = I.second.SubGroups;
15600b57cec5SDimitry Andric     if (!SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty())) {
15610b57cec5SDimitry Andric       OS << "  /* DiagSubGroup" << I.second.IDNo << " */ ";
15620b57cec5SDimitry Andric       for (auto const &SubGroup : SubGroups) {
15630b57cec5SDimitry Andric         std::map<std::string, GroupInfo>::const_iterator RI =
15640b57cec5SDimitry Andric             DiagsInGroup.find(SubGroup);
15650b57cec5SDimitry Andric         assert(RI != DiagsInGroup.end() && "Referenced without existing?");
15660b57cec5SDimitry Andric         OS << RI->second.IDNo << ", ";
15670b57cec5SDimitry Andric       }
15680b57cec5SDimitry Andric       // Emit the groups implicitly in "pedantic".
15690b57cec5SDimitry Andric       if (IsPedantic) {
15700b57cec5SDimitry Andric         for (auto const &Group : GroupsInPedantic) {
15715ffd83dbSDimitry Andric           const std::string &GroupName =
15725ffd83dbSDimitry Andric               std::string(Group->getValueAsString("GroupName"));
15730b57cec5SDimitry Andric           std::map<std::string, GroupInfo>::const_iterator RI =
15740b57cec5SDimitry Andric               DiagsInGroup.find(GroupName);
15750b57cec5SDimitry Andric           assert(RI != DiagsInGroup.end() && "Referenced without existing?");
15760b57cec5SDimitry Andric           OS << RI->second.IDNo << ", ";
15770b57cec5SDimitry Andric         }
15780b57cec5SDimitry Andric       }
15790b57cec5SDimitry Andric 
15800b57cec5SDimitry Andric       OS << "-1,\n";
15810b57cec5SDimitry Andric     }
15820b57cec5SDimitry Andric   }
15830b57cec5SDimitry Andric   OS << "};\n\n";
15840b57cec5SDimitry Andric }
15850b57cec5SDimitry Andric 
15860b57cec5SDimitry Andric /// Emit the list of diagnostic arrays.
15870b57cec5SDimitry Andric ///
15880b57cec5SDimitry Andric /// This data structure is a large array that contains itself arrays of varying
15890b57cec5SDimitry Andric /// size. Each array represents a list of diagnostics. The different arrays are
15900b57cec5SDimitry Andric /// separated by the value '-1'.
15910b57cec5SDimitry Andric ///
15920b57cec5SDimitry Andric /// \code
15930b57cec5SDimitry Andric ///   static const int16_t DiagArrays[] = {
15940b57cec5SDimitry Andric ///     /* Empty */ -1,
15950b57cec5SDimitry Andric ///     /* DiagArray1 */ diag::warn_pragma_message,
15960b57cec5SDimitry Andric ///                      -1,
15970b57cec5SDimitry Andric ///     /* DiagArray2 */ diag::warn_abs_too_small,
15980b57cec5SDimitry Andric ///                      diag::warn_unsigned_abs,
15990b57cec5SDimitry Andric ///                      diag::warn_wrong_absolute_value_type,
16000b57cec5SDimitry Andric ///                      -1
16010b57cec5SDimitry Andric ///   };
16020b57cec5SDimitry Andric /// \endcode
16030b57cec5SDimitry Andric ///
16040b57cec5SDimitry Andric static void emitDiagArrays(std::map<std::string, GroupInfo> &DiagsInGroup,
16050b57cec5SDimitry Andric                            RecordVec &DiagsInPedantic, raw_ostream &OS) {
16060b57cec5SDimitry Andric   OS << "static const int16_t DiagArrays[] = {\n"
16070b57cec5SDimitry Andric      << "  /* Empty */ -1,\n";
16080b57cec5SDimitry Andric   for (auto const &I : DiagsInGroup) {
16090b57cec5SDimitry Andric     const bool IsPedantic = I.first == "pedantic";
16100b57cec5SDimitry Andric 
16110b57cec5SDimitry Andric     const std::vector<const Record *> &V = I.second.DiagsInGroup;
16120b57cec5SDimitry Andric     if (!V.empty() || (IsPedantic && !DiagsInPedantic.empty())) {
16130b57cec5SDimitry Andric       OS << "  /* DiagArray" << I.second.IDNo << " */ ";
16140b57cec5SDimitry Andric       for (auto *Record : V)
16150b57cec5SDimitry Andric         OS << "diag::" << Record->getName() << ", ";
16160b57cec5SDimitry Andric       // Emit the diagnostics implicitly in "pedantic".
16170b57cec5SDimitry Andric       if (IsPedantic) {
16180b57cec5SDimitry Andric         for (auto const &Diag : DiagsInPedantic)
16190b57cec5SDimitry Andric           OS << "diag::" << Diag->getName() << ", ";
16200b57cec5SDimitry Andric       }
16210b57cec5SDimitry Andric       OS << "-1,\n";
16220b57cec5SDimitry Andric     }
16230b57cec5SDimitry Andric   }
16240b57cec5SDimitry Andric   OS << "};\n\n";
16250b57cec5SDimitry Andric }
16260b57cec5SDimitry Andric 
16270b57cec5SDimitry Andric /// Emit a list of group names.
16280b57cec5SDimitry Andric ///
16290b57cec5SDimitry Andric /// This creates a long string which by itself contains a list of pascal style
16300b57cec5SDimitry Andric /// strings, which consist of a length byte directly followed by the string.
16310b57cec5SDimitry Andric ///
16320b57cec5SDimitry Andric /// \code
16330b57cec5SDimitry Andric ///   static const char DiagGroupNames[] = {
16340b57cec5SDimitry Andric ///     \000\020#pragma-messages\t#warnings\020CFString-literal"
16350b57cec5SDimitry Andric ///   };
16360b57cec5SDimitry Andric /// \endcode
16370b57cec5SDimitry Andric static void emitDiagGroupNames(StringToOffsetTable &GroupNames,
16380b57cec5SDimitry Andric                                raw_ostream &OS) {
16390b57cec5SDimitry Andric   OS << "static const char DiagGroupNames[] = {\n";
16400b57cec5SDimitry Andric   GroupNames.EmitString(OS);
16410b57cec5SDimitry Andric   OS << "};\n\n";
16420b57cec5SDimitry Andric }
16430b57cec5SDimitry Andric 
16440b57cec5SDimitry Andric /// Emit diagnostic arrays and related data structures.
16450b57cec5SDimitry Andric ///
16460b57cec5SDimitry Andric /// This creates the actual diagnostic array, an array of diagnostic subgroups
16470b57cec5SDimitry Andric /// and an array of subgroup names.
16480b57cec5SDimitry Andric ///
16490b57cec5SDimitry Andric /// \code
16500b57cec5SDimitry Andric ///  #ifdef GET_DIAG_ARRAYS
16510b57cec5SDimitry Andric ///     static const int16_t DiagArrays[];
16520b57cec5SDimitry Andric ///     static const int16_t DiagSubGroups[];
16530b57cec5SDimitry Andric ///     static const char DiagGroupNames[];
16540b57cec5SDimitry Andric ///  #endif
16550b57cec5SDimitry Andric ///  \endcode
16560b57cec5SDimitry Andric static void emitAllDiagArrays(std::map<std::string, GroupInfo> &DiagsInGroup,
16570b57cec5SDimitry Andric                               RecordVec &DiagsInPedantic,
16580b57cec5SDimitry Andric                               RecordVec &GroupsInPedantic,
16590b57cec5SDimitry Andric                               StringToOffsetTable &GroupNames,
16600b57cec5SDimitry Andric                               raw_ostream &OS) {
16610b57cec5SDimitry Andric   OS << "\n#ifdef GET_DIAG_ARRAYS\n";
16620b57cec5SDimitry Andric   emitDiagArrays(DiagsInGroup, DiagsInPedantic, OS);
16630b57cec5SDimitry Andric   emitDiagSubGroups(DiagsInGroup, GroupsInPedantic, OS);
16640b57cec5SDimitry Andric   emitDiagGroupNames(GroupNames, OS);
16650b57cec5SDimitry Andric   OS << "#endif // GET_DIAG_ARRAYS\n\n";
16660b57cec5SDimitry Andric }
16670b57cec5SDimitry Andric 
16680b57cec5SDimitry Andric /// Emit diagnostic table.
16690b57cec5SDimitry Andric ///
16700b57cec5SDimitry Andric /// The table is sorted by the name of the diagnostic group. Each element
16710b57cec5SDimitry Andric /// consists of the name of the diagnostic group (given as offset in the
16720b57cec5SDimitry Andric /// group name table), a reference to a list of diagnostics (optional) and a
16730b57cec5SDimitry Andric /// reference to a set of subgroups (optional).
16740b57cec5SDimitry Andric ///
16750b57cec5SDimitry Andric /// \code
16760b57cec5SDimitry Andric /// #ifdef GET_DIAG_TABLE
16770b57cec5SDimitry Andric ///  {/* abi */              159, /* DiagArray11 */ 19, /* Empty */          0},
16780b57cec5SDimitry Andric ///  {/* aggregate-return */ 180, /* Empty */        0, /* Empty */          0},
16790b57cec5SDimitry Andric ///  {/* all */              197, /* Empty */        0, /* DiagSubGroup13 */ 3},
16800b57cec5SDimitry Andric ///  {/* deprecated */       1981,/* DiagArray1 */ 348, /* DiagSubGroup3 */  9},
16810b57cec5SDimitry Andric /// #endif
16820b57cec5SDimitry Andric /// \endcode
16830b57cec5SDimitry Andric static void emitDiagTable(std::map<std::string, GroupInfo> &DiagsInGroup,
16840b57cec5SDimitry Andric                           RecordVec &DiagsInPedantic,
16850b57cec5SDimitry Andric                           RecordVec &GroupsInPedantic,
16860b57cec5SDimitry Andric                           StringToOffsetTable &GroupNames, raw_ostream &OS) {
16870b57cec5SDimitry Andric   unsigned MaxLen = 0;
16880b57cec5SDimitry Andric 
16890b57cec5SDimitry Andric   for (auto const &I: DiagsInGroup)
16900b57cec5SDimitry Andric     MaxLen = std::max(MaxLen, (unsigned)I.first.size());
16910b57cec5SDimitry Andric 
1692349cc55cSDimitry Andric   OS << "\n#ifdef DIAG_ENTRY\n";
16930b57cec5SDimitry Andric   unsigned SubGroupIndex = 1, DiagArrayIndex = 1;
16940b57cec5SDimitry Andric   for (auto const &I: DiagsInGroup) {
16950b57cec5SDimitry Andric     // Group option string.
1696349cc55cSDimitry Andric     OS << "DIAG_ENTRY(";
1697349cc55cSDimitry Andric     OS << I.second.GroupName << " /* ";
1698349cc55cSDimitry Andric 
16990b57cec5SDimitry Andric     if (I.first.find_first_not_of("abcdefghijklmnopqrstuvwxyz"
17000b57cec5SDimitry Andric                                    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
17010b57cec5SDimitry Andric                                    "0123456789!@#$%^*-+=:?") !=
17020b57cec5SDimitry Andric         std::string::npos)
17030b57cec5SDimitry Andric       PrintFatalError("Invalid character in diagnostic group '" + I.first +
17040b57cec5SDimitry Andric                       "'");
1705349cc55cSDimitry Andric     OS << I.first << " */, ";
17060b57cec5SDimitry Andric     // Store a pascal-style length byte at the beginning of the string.
17070b57cec5SDimitry Andric     std::string Name = char(I.first.size()) + I.first;
17080b57cec5SDimitry Andric     OS << GroupNames.GetOrAddStringOffset(Name, false) << ", ";
17090b57cec5SDimitry Andric 
17100b57cec5SDimitry Andric     // Special handling for 'pedantic'.
17110b57cec5SDimitry Andric     const bool IsPedantic = I.first == "pedantic";
17120b57cec5SDimitry Andric 
17130b57cec5SDimitry Andric     // Diagnostics in the group.
17140b57cec5SDimitry Andric     const std::vector<const Record *> &V = I.second.DiagsInGroup;
17150b57cec5SDimitry Andric     const bool hasDiags =
17160b57cec5SDimitry Andric         !V.empty() || (IsPedantic && !DiagsInPedantic.empty());
17170b57cec5SDimitry Andric     if (hasDiags) {
17180b57cec5SDimitry Andric       OS << "/* DiagArray" << I.second.IDNo << " */ " << DiagArrayIndex
17190b57cec5SDimitry Andric          << ", ";
17200b57cec5SDimitry Andric       if (IsPedantic)
17210b57cec5SDimitry Andric         DiagArrayIndex += DiagsInPedantic.size();
17220b57cec5SDimitry Andric       DiagArrayIndex += V.size() + 1;
17230b57cec5SDimitry Andric     } else {
1724349cc55cSDimitry Andric       OS << "0, ";
17250b57cec5SDimitry Andric     }
17260b57cec5SDimitry Andric 
17270b57cec5SDimitry Andric     // Subgroups.
17280b57cec5SDimitry Andric     const std::vector<std::string> &SubGroups = I.second.SubGroups;
17290b57cec5SDimitry Andric     const bool hasSubGroups =
17300b57cec5SDimitry Andric         !SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty());
17310b57cec5SDimitry Andric     if (hasSubGroups) {
173281ad6265SDimitry Andric       OS << "/* DiagSubGroup" << I.second.IDNo << " */ " << SubGroupIndex
173381ad6265SDimitry Andric          << ", ";
17340b57cec5SDimitry Andric       if (IsPedantic)
17350b57cec5SDimitry Andric         SubGroupIndex += GroupsInPedantic.size();
17360b57cec5SDimitry Andric       SubGroupIndex += SubGroups.size() + 1;
17370b57cec5SDimitry Andric     } else {
173881ad6265SDimitry Andric       OS << "0, ";
17390b57cec5SDimitry Andric     }
17400b57cec5SDimitry Andric 
174181ad6265SDimitry Andric     std::string Documentation = I.second.Defs.back()
174281ad6265SDimitry Andric                                     ->getValue("Documentation")
174381ad6265SDimitry Andric                                     ->getValue()
174481ad6265SDimitry Andric                                     ->getAsUnquotedString();
174581ad6265SDimitry Andric 
174681ad6265SDimitry Andric     OS << "R\"(" << StringRef(Documentation).trim() << ")\"";
174781ad6265SDimitry Andric 
1748349cc55cSDimitry Andric     OS << ")\n";
17490b57cec5SDimitry Andric   }
1750349cc55cSDimitry Andric   OS << "#endif // DIAG_ENTRY\n\n";
17510b57cec5SDimitry Andric }
17520b57cec5SDimitry Andric 
17530b57cec5SDimitry Andric /// Emit the table of diagnostic categories.
17540b57cec5SDimitry Andric ///
17550b57cec5SDimitry Andric /// The table has the form of macro calls that have two parameters. The
17560b57cec5SDimitry Andric /// category's name as well as an enum that represents the category. The
17570b57cec5SDimitry Andric /// table can be used by defining the macro 'CATEGORY' and including this
17580b57cec5SDimitry Andric /// table right after.
17590b57cec5SDimitry Andric ///
17600b57cec5SDimitry Andric /// \code
17610b57cec5SDimitry Andric /// #ifdef GET_CATEGORY_TABLE
17620b57cec5SDimitry Andric ///   CATEGORY("Semantic Issue", DiagCat_Semantic_Issue)
17630b57cec5SDimitry Andric ///   CATEGORY("Lambda Issue", DiagCat_Lambda_Issue)
17640b57cec5SDimitry Andric /// #endif
17650b57cec5SDimitry Andric /// \endcode
17660b57cec5SDimitry Andric static void emitCategoryTable(RecordKeeper &Records, raw_ostream &OS) {
17670b57cec5SDimitry Andric   DiagCategoryIDMap CategoriesByID(Records);
17680b57cec5SDimitry Andric   OS << "\n#ifdef GET_CATEGORY_TABLE\n";
17690b57cec5SDimitry Andric   for (auto const &C : CategoriesByID)
17700b57cec5SDimitry Andric     OS << "CATEGORY(\"" << C << "\", " << getDiagCategoryEnum(C) << ")\n";
17710b57cec5SDimitry Andric   OS << "#endif // GET_CATEGORY_TABLE\n\n";
17720b57cec5SDimitry Andric }
17730b57cec5SDimitry Andric 
1774a7dea167SDimitry Andric void clang::EmitClangDiagGroups(RecordKeeper &Records, raw_ostream &OS) {
17750b57cec5SDimitry Andric   // Compute a mapping from a DiagGroup to all of its parents.
17760b57cec5SDimitry Andric   DiagGroupParentMap DGParentMap(Records);
17770b57cec5SDimitry Andric 
17780b57cec5SDimitry Andric   std::vector<Record *> Diags = Records.getAllDerivedDefinitions("Diagnostic");
17790b57cec5SDimitry Andric 
17800b57cec5SDimitry Andric   std::vector<Record *> DiagGroups =
17810b57cec5SDimitry Andric       Records.getAllDerivedDefinitions("DiagGroup");
17820b57cec5SDimitry Andric 
17830b57cec5SDimitry Andric   std::map<std::string, GroupInfo> DiagsInGroup;
17840b57cec5SDimitry Andric   groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
17850b57cec5SDimitry Andric 
17860b57cec5SDimitry Andric   // All extensions are implicitly in the "pedantic" group.  Record the
17870b57cec5SDimitry Andric   // implicit set of groups in the "pedantic" group, and use this information
17880b57cec5SDimitry Andric   // later when emitting the group information for Pedantic.
17890b57cec5SDimitry Andric   RecordVec DiagsInPedantic;
17900b57cec5SDimitry Andric   RecordVec GroupsInPedantic;
17910b57cec5SDimitry Andric   InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
17920b57cec5SDimitry Andric   inferPedantic.compute(&DiagsInPedantic, &GroupsInPedantic);
17930b57cec5SDimitry Andric 
17940b57cec5SDimitry Andric   StringToOffsetTable GroupNames;
17950b57cec5SDimitry Andric   for (std::map<std::string, GroupInfo>::const_iterator
17960b57cec5SDimitry Andric            I = DiagsInGroup.begin(),
17970b57cec5SDimitry Andric            E = DiagsInGroup.end();
17980b57cec5SDimitry Andric        I != E; ++I) {
17990b57cec5SDimitry Andric     // Store a pascal-style length byte at the beginning of the string.
18000b57cec5SDimitry Andric     std::string Name = char(I->first.size()) + I->first;
18010b57cec5SDimitry Andric     GroupNames.GetOrAddStringOffset(Name, false);
18020b57cec5SDimitry Andric   }
18030b57cec5SDimitry Andric 
18040b57cec5SDimitry Andric   emitAllDiagArrays(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames,
18050b57cec5SDimitry Andric                     OS);
18060b57cec5SDimitry Andric   emitDiagTable(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames,
18070b57cec5SDimitry Andric                 OS);
18080b57cec5SDimitry Andric   emitCategoryTable(Records, OS);
18090b57cec5SDimitry Andric }
18100b57cec5SDimitry Andric 
18110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
18120b57cec5SDimitry Andric // Diagnostic name index generation
18130b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
18140b57cec5SDimitry Andric 
18150b57cec5SDimitry Andric namespace {
18160b57cec5SDimitry Andric struct RecordIndexElement
18170b57cec5SDimitry Andric {
18180b57cec5SDimitry Andric   RecordIndexElement() {}
18195ffd83dbSDimitry Andric   explicit RecordIndexElement(Record const &R)
18205ffd83dbSDimitry Andric       : Name(std::string(R.getName())) {}
18210b57cec5SDimitry Andric 
18220b57cec5SDimitry Andric   std::string Name;
18230b57cec5SDimitry Andric };
18240b57cec5SDimitry Andric } // end anonymous namespace.
18250b57cec5SDimitry Andric 
1826a7dea167SDimitry Andric void clang::EmitClangDiagsIndexName(RecordKeeper &Records, raw_ostream &OS) {
18270b57cec5SDimitry Andric   const std::vector<Record*> &Diags =
18280b57cec5SDimitry Andric     Records.getAllDerivedDefinitions("Diagnostic");
18290b57cec5SDimitry Andric 
18300b57cec5SDimitry Andric   std::vector<RecordIndexElement> Index;
18310b57cec5SDimitry Andric   Index.reserve(Diags.size());
18320b57cec5SDimitry Andric   for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
18330b57cec5SDimitry Andric     const Record &R = *(Diags[i]);
18340b57cec5SDimitry Andric     Index.push_back(RecordIndexElement(R));
18350b57cec5SDimitry Andric   }
18360b57cec5SDimitry Andric 
18370b57cec5SDimitry Andric   llvm::sort(Index,
18380b57cec5SDimitry Andric              [](const RecordIndexElement &Lhs, const RecordIndexElement &Rhs) {
18390b57cec5SDimitry Andric                return Lhs.Name < Rhs.Name;
18400b57cec5SDimitry Andric              });
18410b57cec5SDimitry Andric 
18420b57cec5SDimitry Andric   for (unsigned i = 0, e = Index.size(); i != e; ++i) {
18430b57cec5SDimitry Andric     const RecordIndexElement &R = Index[i];
18440b57cec5SDimitry Andric 
18450b57cec5SDimitry Andric     OS << "DIAG_NAME_INDEX(" << R.Name << ")\n";
18460b57cec5SDimitry Andric   }
18470b57cec5SDimitry Andric }
18480b57cec5SDimitry Andric 
18490b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
18500b57cec5SDimitry Andric // Diagnostic documentation generation
18510b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
18520b57cec5SDimitry Andric 
18530b57cec5SDimitry Andric namespace docs {
18540b57cec5SDimitry Andric namespace {
18550b57cec5SDimitry Andric 
18560b57cec5SDimitry Andric bool isRemarkGroup(const Record *DiagGroup,
18570b57cec5SDimitry Andric                    const std::map<std::string, GroupInfo> &DiagsInGroup) {
18580b57cec5SDimitry Andric   bool AnyRemarks = false, AnyNonRemarks = false;
18590b57cec5SDimitry Andric 
18600b57cec5SDimitry Andric   std::function<void(StringRef)> Visit = [&](StringRef GroupName) {
18615ffd83dbSDimitry Andric     auto &GroupInfo = DiagsInGroup.find(std::string(GroupName))->second;
18620b57cec5SDimitry Andric     for (const Record *Diag : GroupInfo.DiagsInGroup)
18630b57cec5SDimitry Andric       (isRemark(*Diag) ? AnyRemarks : AnyNonRemarks) = true;
18640b57cec5SDimitry Andric     for (const auto &Name : GroupInfo.SubGroups)
18650b57cec5SDimitry Andric       Visit(Name);
18660b57cec5SDimitry Andric   };
18670b57cec5SDimitry Andric   Visit(DiagGroup->getValueAsString("GroupName"));
18680b57cec5SDimitry Andric 
18690b57cec5SDimitry Andric   if (AnyRemarks && AnyNonRemarks)
18700b57cec5SDimitry Andric     PrintFatalError(
18710b57cec5SDimitry Andric         DiagGroup->getLoc(),
18720b57cec5SDimitry Andric         "Diagnostic group contains both remark and non-remark diagnostics");
18730b57cec5SDimitry Andric   return AnyRemarks;
18740b57cec5SDimitry Andric }
18750b57cec5SDimitry Andric 
18760b57cec5SDimitry Andric std::string getDefaultSeverity(const Record *Diag) {
18775ffd83dbSDimitry Andric   return std::string(
18785ffd83dbSDimitry Andric       Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name"));
18790b57cec5SDimitry Andric }
18800b57cec5SDimitry Andric 
18810b57cec5SDimitry Andric std::set<std::string>
18820b57cec5SDimitry Andric getDefaultSeverities(const Record *DiagGroup,
18830b57cec5SDimitry Andric                      const std::map<std::string, GroupInfo> &DiagsInGroup) {
18840b57cec5SDimitry Andric   std::set<std::string> States;
18850b57cec5SDimitry Andric 
18860b57cec5SDimitry Andric   std::function<void(StringRef)> Visit = [&](StringRef GroupName) {
18875ffd83dbSDimitry Andric     auto &GroupInfo = DiagsInGroup.find(std::string(GroupName))->second;
18880b57cec5SDimitry Andric     for (const Record *Diag : GroupInfo.DiagsInGroup)
18890b57cec5SDimitry Andric       States.insert(getDefaultSeverity(Diag));
18900b57cec5SDimitry Andric     for (const auto &Name : GroupInfo.SubGroups)
18910b57cec5SDimitry Andric       Visit(Name);
18920b57cec5SDimitry Andric   };
18930b57cec5SDimitry Andric   Visit(DiagGroup->getValueAsString("GroupName"));
18940b57cec5SDimitry Andric   return States;
18950b57cec5SDimitry Andric }
18960b57cec5SDimitry Andric 
18970b57cec5SDimitry Andric void writeHeader(StringRef Str, raw_ostream &OS, char Kind = '-') {
18980b57cec5SDimitry Andric   OS << Str << "\n" << std::string(Str.size(), Kind) << "\n";
18990b57cec5SDimitry Andric }
19000b57cec5SDimitry Andric 
19010b57cec5SDimitry Andric void writeDiagnosticText(DiagnosticTextBuilder &Builder, const Record *R,
19020b57cec5SDimitry Andric                          StringRef Role, raw_ostream &OS) {
1903bdd1243dSDimitry Andric   StringRef Text = R->getValueAsString("Summary");
19040b57cec5SDimitry Andric   if (Text == "%0")
19050b57cec5SDimitry Andric     OS << "The text of this diagnostic is not controlled by Clang.\n\n";
19060b57cec5SDimitry Andric   else {
19070b57cec5SDimitry Andric     std::vector<std::string> Out = Builder.buildForDocumentation(Role, R);
19080b57cec5SDimitry Andric     for (auto &Line : Out)
19090b57cec5SDimitry Andric       OS << Line << "\n";
19100b57cec5SDimitry Andric     OS << "\n";
19110b57cec5SDimitry Andric   }
19120b57cec5SDimitry Andric }
19130b57cec5SDimitry Andric 
19140b57cec5SDimitry Andric }  // namespace
19150b57cec5SDimitry Andric }  // namespace docs
19160b57cec5SDimitry Andric 
1917a7dea167SDimitry Andric void clang::EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS) {
19180b57cec5SDimitry Andric   using namespace docs;
19190b57cec5SDimitry Andric 
19200b57cec5SDimitry Andric   // Get the documentation introduction paragraph.
19210b57cec5SDimitry Andric   const Record *Documentation = Records.getDef("GlobalDocumentation");
19220b57cec5SDimitry Andric   if (!Documentation) {
19230b57cec5SDimitry Andric     PrintFatalError("The Documentation top-level definition is missing, "
19240b57cec5SDimitry Andric                     "no documentation will be generated.");
19250b57cec5SDimitry Andric     return;
19260b57cec5SDimitry Andric   }
19270b57cec5SDimitry Andric 
19280b57cec5SDimitry Andric   OS << Documentation->getValueAsString("Intro") << "\n";
19290b57cec5SDimitry Andric 
19300b57cec5SDimitry Andric   DiagnosticTextBuilder Builder(Records);
19310b57cec5SDimitry Andric 
19320b57cec5SDimitry Andric   std::vector<Record*> Diags =
19330b57cec5SDimitry Andric       Records.getAllDerivedDefinitions("Diagnostic");
19340b57cec5SDimitry Andric 
19350b57cec5SDimitry Andric   std::vector<Record*> DiagGroups =
19360b57cec5SDimitry Andric       Records.getAllDerivedDefinitions("DiagGroup");
19370b57cec5SDimitry Andric   llvm::sort(DiagGroups, diagGroupBeforeByName);
19380b57cec5SDimitry Andric 
19390b57cec5SDimitry Andric   DiagGroupParentMap DGParentMap(Records);
19400b57cec5SDimitry Andric 
19410b57cec5SDimitry Andric   std::map<std::string, GroupInfo> DiagsInGroup;
19420b57cec5SDimitry Andric   groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
19430b57cec5SDimitry Andric 
19440b57cec5SDimitry Andric   // Compute the set of diagnostics that are in -Wpedantic.
19450b57cec5SDimitry Andric   {
19460b57cec5SDimitry Andric     RecordSet DiagsInPedanticSet;
19470b57cec5SDimitry Andric     RecordSet GroupsInPedanticSet;
19480b57cec5SDimitry Andric     InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
19490b57cec5SDimitry Andric     inferPedantic.compute(&DiagsInPedanticSet, &GroupsInPedanticSet);
19500b57cec5SDimitry Andric     auto &PedDiags = DiagsInGroup["pedantic"];
19510b57cec5SDimitry Andric     // Put the diagnostics into a deterministic order.
19520b57cec5SDimitry Andric     RecordVec DiagsInPedantic(DiagsInPedanticSet.begin(),
19530b57cec5SDimitry Andric                               DiagsInPedanticSet.end());
19540b57cec5SDimitry Andric     RecordVec GroupsInPedantic(GroupsInPedanticSet.begin(),
19550b57cec5SDimitry Andric                                GroupsInPedanticSet.end());
19560b57cec5SDimitry Andric     llvm::sort(DiagsInPedantic, beforeThanCompare);
19570b57cec5SDimitry Andric     llvm::sort(GroupsInPedantic, beforeThanCompare);
19580b57cec5SDimitry Andric     PedDiags.DiagsInGroup.insert(PedDiags.DiagsInGroup.end(),
19590b57cec5SDimitry Andric                                  DiagsInPedantic.begin(),
19600b57cec5SDimitry Andric                                  DiagsInPedantic.end());
19610b57cec5SDimitry Andric     for (auto *Group : GroupsInPedantic)
19625ffd83dbSDimitry Andric       PedDiags.SubGroups.push_back(
19635ffd83dbSDimitry Andric           std::string(Group->getValueAsString("GroupName")));
19640b57cec5SDimitry Andric   }
19650b57cec5SDimitry Andric 
19660b57cec5SDimitry Andric   // FIXME: Write diagnostic categories and link to diagnostic groups in each.
19670b57cec5SDimitry Andric 
19680b57cec5SDimitry Andric   // Write out the diagnostic groups.
19690b57cec5SDimitry Andric   for (const Record *G : DiagGroups) {
19700b57cec5SDimitry Andric     bool IsRemarkGroup = isRemarkGroup(G, DiagsInGroup);
19715ffd83dbSDimitry Andric     auto &GroupInfo =
19725ffd83dbSDimitry Andric         DiagsInGroup[std::string(G->getValueAsString("GroupName"))];
19730b57cec5SDimitry Andric     bool IsSynonym = GroupInfo.DiagsInGroup.empty() &&
19740b57cec5SDimitry Andric                      GroupInfo.SubGroups.size() == 1;
19750b57cec5SDimitry Andric 
19760b57cec5SDimitry Andric     writeHeader(((IsRemarkGroup ? "-R" : "-W") +
19770b57cec5SDimitry Andric                     G->getValueAsString("GroupName")).str(),
19780b57cec5SDimitry Andric                 OS);
19790b57cec5SDimitry Andric 
19800b57cec5SDimitry Andric     if (!IsSynonym) {
19810b57cec5SDimitry Andric       // FIXME: Ideally, all the diagnostics in a group should have the same
19820b57cec5SDimitry Andric       // default state, but that is not currently the case.
19830b57cec5SDimitry Andric       auto DefaultSeverities = getDefaultSeverities(G, DiagsInGroup);
19840b57cec5SDimitry Andric       if (!DefaultSeverities.empty() && !DefaultSeverities.count("Ignored")) {
19850b57cec5SDimitry Andric         bool AnyNonErrors = DefaultSeverities.count("Warning") ||
19860b57cec5SDimitry Andric                             DefaultSeverities.count("Remark");
19870b57cec5SDimitry Andric         if (!AnyNonErrors)
19880b57cec5SDimitry Andric           OS << "This diagnostic is an error by default, but the flag ``-Wno-"
19890b57cec5SDimitry Andric              << G->getValueAsString("GroupName") << "`` can be used to disable "
19900b57cec5SDimitry Andric              << "the error.\n\n";
19910b57cec5SDimitry Andric         else
19920b57cec5SDimitry Andric           OS << "This diagnostic is enabled by default.\n\n";
19930b57cec5SDimitry Andric       } else if (DefaultSeverities.size() > 1) {
19940b57cec5SDimitry Andric         OS << "Some of the diagnostics controlled by this flag are enabled "
19950b57cec5SDimitry Andric            << "by default.\n\n";
19960b57cec5SDimitry Andric       }
19970b57cec5SDimitry Andric     }
19980b57cec5SDimitry Andric 
19990b57cec5SDimitry Andric     if (!GroupInfo.SubGroups.empty()) {
20000b57cec5SDimitry Andric       if (IsSynonym)
20010b57cec5SDimitry Andric         OS << "Synonym for ";
20020b57cec5SDimitry Andric       else if (GroupInfo.DiagsInGroup.empty())
20030b57cec5SDimitry Andric         OS << "Controls ";
20040b57cec5SDimitry Andric       else
20050b57cec5SDimitry Andric         OS << "Also controls ";
20060b57cec5SDimitry Andric 
20070b57cec5SDimitry Andric       bool First = true;
20080b57cec5SDimitry Andric       llvm::sort(GroupInfo.SubGroups);
20090b57cec5SDimitry Andric       for (const auto &Name : GroupInfo.SubGroups) {
20100b57cec5SDimitry Andric         if (!First) OS << ", ";
20110b57cec5SDimitry Andric         OS << "`" << (IsRemarkGroup ? "-R" : "-W") << Name << "`_";
20120b57cec5SDimitry Andric         First = false;
20130b57cec5SDimitry Andric       }
20140b57cec5SDimitry Andric       OS << ".\n\n";
20150b57cec5SDimitry Andric     }
20160b57cec5SDimitry Andric 
20170b57cec5SDimitry Andric     if (!GroupInfo.DiagsInGroup.empty()) {
20180b57cec5SDimitry Andric       OS << "**Diagnostic text:**\n\n";
20190b57cec5SDimitry Andric       for (const Record *D : GroupInfo.DiagsInGroup) {
20200b57cec5SDimitry Andric         auto Severity = getDefaultSeverity(D);
20210b57cec5SDimitry Andric         Severity[0] = tolower(Severity[0]);
20220b57cec5SDimitry Andric         if (Severity == "ignored")
20230b57cec5SDimitry Andric           Severity = IsRemarkGroup ? "remark" : "warning";
20240b57cec5SDimitry Andric 
20250b57cec5SDimitry Andric         writeDiagnosticText(Builder, D, Severity, OS);
20260b57cec5SDimitry Andric       }
20270b57cec5SDimitry Andric     }
20280b57cec5SDimitry Andric 
20290b57cec5SDimitry Andric     auto Doc = G->getValueAsString("Documentation");
20300b57cec5SDimitry Andric     if (!Doc.empty())
20310b57cec5SDimitry Andric       OS << Doc;
20320b57cec5SDimitry Andric     else if (GroupInfo.SubGroups.empty() && GroupInfo.DiagsInGroup.empty())
20330b57cec5SDimitry Andric       OS << "This diagnostic flag exists for GCC compatibility, and has no "
20340b57cec5SDimitry Andric             "effect in Clang.\n";
20350b57cec5SDimitry Andric     OS << "\n";
20360b57cec5SDimitry Andric   }
20370b57cec5SDimitry Andric }
2038