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