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