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