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