1 | //===-- ClangDiagnosticsEmitter.cpp - Generate Clang diagnostics tables ---===// |
---|---|
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/SmallString.h" |
18 | #include "llvm/ADT/SmallVector.h" |
19 | #include "llvm/ADT/StringMap.h" |
20 | #include "llvm/ADT/StringSwitch.h" |
21 | #include "llvm/ADT/Twine.h" |
22 | #include "llvm/Support/Casting.h" |
23 | #include "llvm/Support/Format.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 | const RecordKeeper &Records; |
43 | std::map<const Record *, std::vector<const Record *>> Mapping; |
44 | |
45 | public: |
46 | DiagGroupParentMap(const RecordKeeper &records) : Records(records) { |
47 | for (const Record *Group : Records.getAllDerivedDefinitions(ClassName: "DiagGroup")) |
48 | for (const Record *SubGroup : Group->getValueAsListOfDefs(FieldName: "SubGroups")) |
49 | Mapping[SubGroup].push_back(x: Group); |
50 | } |
51 | |
52 | ArrayRef<const Record *> getParents(const Record *Group) { |
53 | return Mapping[Group]; |
54 | } |
55 | }; |
56 | } // end anonymous namespace. |
57 | |
58 | static StringRef |
59 | getCategoryFromDiagGroup(const Record *Group, |
60 | DiagGroupParentMap &DiagGroupParents) { |
61 | // If the DiagGroup has a category, return it. |
62 | StringRef CatName = Group->getValueAsString(FieldName: "CategoryName"); |
63 | if (!CatName.empty()) return CatName; |
64 | |
65 | // The diag group may the subgroup of one or more other diagnostic groups, |
66 | // check these for a category as well. |
67 | for (const Record *Parent : DiagGroupParents.getParents(Group)) { |
68 | CatName = getCategoryFromDiagGroup(Group: Parent, DiagGroupParents); |
69 | if (!CatName.empty()) return CatName; |
70 | } |
71 | return ""; |
72 | } |
73 | |
74 | /// getDiagnosticCategory - Return the category that the specified diagnostic |
75 | /// lives in. |
76 | static StringRef getDiagnosticCategory(const Record *R, |
77 | DiagGroupParentMap &DiagGroupParents) { |
78 | // If the diagnostic is in a group, and that group has a category, use it. |
79 | if (const auto *Group = dyn_cast<DefInit>(Val: R->getValueInit(FieldName: "Group"))) { |
80 | // Check the diagnostic's diag group for a category. |
81 | StringRef CatName = |
82 | getCategoryFromDiagGroup(Group: Group->getDef(), DiagGroupParents); |
83 | if (!CatName.empty()) return CatName; |
84 | } |
85 | |
86 | // If the diagnostic itself has a category, get it. |
87 | return R->getValueAsString(FieldName: "CategoryName"); |
88 | } |
89 | |
90 | namespace { |
91 | class DiagCategoryIDMap { |
92 | const RecordKeeper &Records; |
93 | StringMap<unsigned> CategoryIDs; |
94 | std::vector<StringRef> CategoryStrings; |
95 | |
96 | public: |
97 | DiagCategoryIDMap(const RecordKeeper &records) : Records(records) { |
98 | DiagGroupParentMap ParentInfo(Records); |
99 | |
100 | // The zero'th category is "". |
101 | CategoryStrings.push_back(x: ""); |
102 | CategoryIDs[""] = 0; |
103 | |
104 | for (const Record *Diag : |
105 | Records.getAllDerivedDefinitions(ClassName: "Diagnostic")) { |
106 | StringRef Category = getDiagnosticCategory(R: Diag, DiagGroupParents&: ParentInfo); |
107 | if (Category.empty()) continue; // Skip diags with no category. |
108 | |
109 | unsigned &ID = CategoryIDs[Category]; |
110 | if (ID != 0) continue; // Already seen. |
111 | |
112 | ID = CategoryStrings.size(); |
113 | CategoryStrings.push_back(x: Category); |
114 | } |
115 | } |
116 | |
117 | unsigned getID(StringRef CategoryString) { |
118 | return CategoryIDs[CategoryString]; |
119 | } |
120 | |
121 | typedef std::vector<StringRef>::const_iterator const_iterator; |
122 | const_iterator begin() const { return CategoryStrings.begin(); } |
123 | const_iterator end() const { return CategoryStrings.end(); } |
124 | }; |
125 | |
126 | struct GroupInfo { |
127 | StringRef GroupName; |
128 | std::vector<const Record*> DiagsInGroup; |
129 | std::vector<StringRef> SubGroups; |
130 | unsigned IDNo = 0; |
131 | |
132 | SmallVector<const Record *, 1> Defs; |
133 | |
134 | GroupInfo() = default; |
135 | }; |
136 | } // end anonymous namespace. |
137 | |
138 | static bool beforeThanCompare(const Record *LHS, const Record *RHS) { |
139 | assert(!LHS->getLoc().empty() && !RHS->getLoc().empty()); |
140 | return |
141 | LHS->getLoc().front().getPointer() < RHS->getLoc().front().getPointer(); |
142 | } |
143 | |
144 | static bool diagGroupBeforeByName(const Record *LHS, const Record *RHS) { |
145 | return LHS->getValueAsString(FieldName: "GroupName") < |
146 | RHS->getValueAsString(FieldName: "GroupName"); |
147 | } |
148 | |
149 | using DiagsInGroupTy = std::map<StringRef, GroupInfo>; |
150 | |
151 | /// Invert the 1-[0/1] mapping of diags to group into a one to many |
152 | /// mapping of groups to diags in the group. |
153 | static void groupDiagnostics(ArrayRef<const Record *> Diags, |
154 | ArrayRef<const Record *> DiagGroups, |
155 | DiagsInGroupTy &DiagsInGroup) { |
156 | for (const Record *R : Diags) { |
157 | const auto *DI = dyn_cast<DefInit>(Val: R->getValueInit(FieldName: "Group")); |
158 | if (!DI) |
159 | continue; |
160 | assert(R->getValueAsDef("Class")->getName() != "CLASS_NOTE"&& |
161 | "Note can't be in a DiagGroup"); |
162 | StringRef GroupName = DI->getDef()->getValueAsString(FieldName: "GroupName"); |
163 | DiagsInGroup[GroupName].DiagsInGroup.push_back(x: R); |
164 | } |
165 | |
166 | // Add all DiagGroup's to the DiagsInGroup list to make sure we pick up empty |
167 | // groups (these are warnings that GCC supports that clang never produces). |
168 | for (const Record *Group : DiagGroups) { |
169 | GroupInfo &GI = DiagsInGroup[Group->getValueAsString(FieldName: "GroupName")]; |
170 | GI.GroupName = Group->getName(); |
171 | GI.Defs.push_back(Elt: Group); |
172 | |
173 | for (const Record *SubGroup : Group->getValueAsListOfDefs(FieldName: "SubGroups")) |
174 | GI.SubGroups.push_back(x: SubGroup->getValueAsString(FieldName: "GroupName")); |
175 | } |
176 | |
177 | // Assign unique ID numbers to the groups. |
178 | for (auto [IdNo, Iter] : enumerate(First&: DiagsInGroup)) |
179 | Iter.second.IDNo = IdNo; |
180 | |
181 | // Warn if the same group is defined more than once (including implicitly). |
182 | for (auto &Group : DiagsInGroup) { |
183 | if (Group.second.Defs.size() == 1 && |
184 | (!Group.second.Defs.front()->isAnonymous() || |
185 | Group.second.DiagsInGroup.size() <= 1)) |
186 | continue; |
187 | |
188 | bool First = true; |
189 | for (const Record *Def : Group.second.Defs) { |
190 | // Skip implicit definitions from diagnostics; we'll report those |
191 | // separately below. |
192 | bool IsImplicit = false; |
193 | for (const Record *Diag : Group.second.DiagsInGroup) { |
194 | if (cast<DefInit>(Val: Diag->getValueInit(FieldName: "Group"))->getDef() == Def) { |
195 | IsImplicit = true; |
196 | break; |
197 | } |
198 | } |
199 | if (IsImplicit) |
200 | continue; |
201 | |
202 | SMLoc Loc = Def->getLoc().front(); |
203 | if (First) { |
204 | SrcMgr.PrintMessage(Loc, Kind: SourceMgr::DK_Error, |
205 | Msg: Twine("group '") + Group.first + |
206 | "' is defined more than once"); |
207 | First = false; |
208 | } else { |
209 | SrcMgr.PrintMessage(Loc, Kind: SourceMgr::DK_Note, Msg: "also defined here"); |
210 | } |
211 | } |
212 | |
213 | for (const Record *Diag : Group.second.DiagsInGroup) { |
214 | if (!cast<DefInit>(Val: Diag->getValueInit(FieldName: "Group"))->getDef()->isAnonymous()) |
215 | continue; |
216 | |
217 | SMLoc Loc = Diag->getLoc().front(); |
218 | if (First) { |
219 | SrcMgr.PrintMessage(Loc, Kind: SourceMgr::DK_Error, |
220 | Msg: Twine("group '") + Group.first + |
221 | "' is implicitly defined more than once"); |
222 | First = false; |
223 | } else { |
224 | SrcMgr.PrintMessage(Loc, Kind: SourceMgr::DK_Note, |
225 | Msg: "also implicitly defined here"); |
226 | } |
227 | } |
228 | } |
229 | } |
230 | |
231 | //===----------------------------------------------------------------------===// |
232 | // Infer members of -Wpedantic. |
233 | //===----------------------------------------------------------------------===// |
234 | |
235 | typedef std::vector<const Record *> RecordVec; |
236 | typedef DenseSet<const Record *> RecordSet; |
237 | typedef PointerUnion<RecordVec *, RecordSet *> VecOrSet; |
238 | |
239 | namespace { |
240 | class InferPedantic { |
241 | typedef DenseMap<const Record *, std::pair<unsigned, std::optional<unsigned>>> |
242 | GMap; |
243 | |
244 | DiagGroupParentMap &DiagGroupParents; |
245 | ArrayRef<const Record *> Diags; |
246 | const std::vector<const Record *> DiagGroups; |
247 | DiagsInGroupTy &DiagsInGroup; |
248 | DenseSet<const Record *> DiagsSet; |
249 | GMap GroupCount; |
250 | public: |
251 | InferPedantic(DiagGroupParentMap &DiagGroupParents, |
252 | ArrayRef<const Record *> Diags, |
253 | ArrayRef<const Record *> DiagGroups, |
254 | DiagsInGroupTy &DiagsInGroup) |
255 | : DiagGroupParents(DiagGroupParents), Diags(Diags), |
256 | DiagGroups(DiagGroups), DiagsInGroup(DiagsInGroup) {} |
257 | |
258 | /// Compute the set of diagnostics and groups that are immediately |
259 | /// in -Wpedantic. |
260 | void compute(VecOrSet DiagsInPedantic, |
261 | VecOrSet GroupsInPedantic); |
262 | |
263 | private: |
264 | /// Determine whether a group is a subgroup of another group. |
265 | bool isSubGroupOfGroup(const Record *Group, StringRef RootGroupName); |
266 | |
267 | /// Determine if the diagnostic is an extension. |
268 | bool isExtension(const Record *Diag); |
269 | |
270 | /// Determine if the diagnostic is off by default. |
271 | bool isOffByDefault(const Record *Diag); |
272 | |
273 | /// Increment the count for a group, and transitively marked |
274 | /// parent groups when appropriate. |
275 | void markGroup(const Record *Group); |
276 | |
277 | /// Return true if the diagnostic is in a pedantic group. |
278 | bool groupInPedantic(const Record *Group, bool increment = false); |
279 | }; |
280 | } // end anonymous namespace |
281 | |
282 | bool InferPedantic::isSubGroupOfGroup(const Record *Group, StringRef GName) { |
283 | StringRef GroupName = Group->getValueAsString(FieldName: "GroupName"); |
284 | if (GName == GroupName) |
285 | return true; |
286 | |
287 | for (const Record *Parent : DiagGroupParents.getParents(Group)) |
288 | if (isSubGroupOfGroup(Group: Parent, GName)) |
289 | return true; |
290 | |
291 | return false; |
292 | } |
293 | |
294 | /// Determine if the diagnostic is an extension. |
295 | bool InferPedantic::isExtension(const Record *Diag) { |
296 | return Diag->getValueAsDef(FieldName: "Class")->getName() == "CLASS_EXTENSION"; |
297 | } |
298 | |
299 | bool InferPedantic::isOffByDefault(const Record *Diag) { |
300 | return Diag->getValueAsDef(FieldName: "DefaultSeverity")->getValueAsString(FieldName: "Name") == |
301 | "Ignored"; |
302 | } |
303 | |
304 | bool InferPedantic::groupInPedantic(const Record *Group, bool increment) { |
305 | GMap::mapped_type &V = GroupCount[Group]; |
306 | // Lazily compute the threshold value for the group count. |
307 | if (!V.second) { |
308 | const GroupInfo &GI = DiagsInGroup[Group->getValueAsString(FieldName: "GroupName")]; |
309 | V.second = GI.SubGroups.size() + GI.DiagsInGroup.size(); |
310 | } |
311 | |
312 | if (increment) |
313 | ++V.first; |
314 | |
315 | // Consider a group in -Wpendatic IFF if has at least one diagnostic |
316 | // or subgroup AND all of those diagnostics and subgroups are covered |
317 | // by -Wpedantic via our computation. |
318 | return V.first != 0 && V.first == *V.second; |
319 | } |
320 | |
321 | void InferPedantic::markGroup(const Record *Group) { |
322 | // If all the diagnostics and subgroups have been marked as being |
323 | // covered by -Wpedantic, increment the count of parent groups. Once the |
324 | // group's count is equal to the number of subgroups and diagnostics in |
325 | // that group, we can safely add this group to -Wpedantic. |
326 | if (groupInPedantic(Group, /* increment */ true)) |
327 | for (const Record *Parent : DiagGroupParents.getParents(Group)) |
328 | markGroup(Group: Parent); |
329 | } |
330 | |
331 | void InferPedantic::compute(VecOrSet DiagsInPedantic, |
332 | VecOrSet GroupsInPedantic) { |
333 | // All extensions that are not on by default are implicitly in the |
334 | // "pedantic" group. For those that aren't explicitly included in -Wpedantic, |
335 | // mark them for consideration to be included in -Wpedantic directly. |
336 | for (const Record *R : Diags) { |
337 | if (!isExtension(Diag: R) || !isOffByDefault(Diag: R)) |
338 | continue; |
339 | DiagsSet.insert(V: R); |
340 | if (const auto *Group = dyn_cast<DefInit>(Val: R->getValueInit(FieldName: "Group"))) { |
341 | const Record *GroupRec = Group->getDef(); |
342 | if (!isSubGroupOfGroup(Group: GroupRec, GName: "pedantic")) { |
343 | markGroup(Group: GroupRec); |
344 | } |
345 | } |
346 | } |
347 | |
348 | // Compute the set of diagnostics that are directly in -Wpedantic. We |
349 | // march through Diags a second time to ensure the results are emitted |
350 | // in deterministic order. |
351 | for (const Record *R : Diags) { |
352 | if (!DiagsSet.count(V: R)) |
353 | continue; |
354 | // Check if the group is implicitly in -Wpedantic. If so, |
355 | // the diagnostic should not be directly included in the -Wpedantic |
356 | // diagnostic group. |
357 | if (const auto *Group = dyn_cast<DefInit>(Val: R->getValueInit(FieldName: "Group"))) |
358 | if (groupInPedantic(Group: Group->getDef())) |
359 | continue; |
360 | |
361 | // The diagnostic is not included in a group that is (transitively) in |
362 | // -Wpedantic. Include it in -Wpedantic directly. |
363 | if (auto *V = dyn_cast<RecordVec *>(Val&: DiagsInPedantic)) |
364 | V->push_back(x: R); |
365 | else |
366 | cast<RecordSet *>(Val&: DiagsInPedantic)->insert(V: R); |
367 | } |
368 | |
369 | if (!GroupsInPedantic) |
370 | return; |
371 | |
372 | // Compute the set of groups that are directly in -Wpedantic. We |
373 | // march through the groups to ensure the results are emitted |
374 | /// in a deterministc order. |
375 | for (const Record *Group : DiagGroups) { |
376 | if (!groupInPedantic(Group)) |
377 | continue; |
378 | |
379 | const std::vector<const Record *> &Parents = |
380 | DiagGroupParents.getParents(Group); |
381 | bool AllParentsInPedantic = |
382 | all_of(Range: Parents, P: [&](const Record *R) { return groupInPedantic(Group: R); }); |
383 | // If all the parents are in -Wpedantic, this means that this diagnostic |
384 | // group will be indirectly included by -Wpedantic already. In that |
385 | // case, do not add it directly to -Wpedantic. If the group has no |
386 | // parents, obviously it should go into -Wpedantic. |
387 | if (Parents.size() > 0 && AllParentsInPedantic) |
388 | continue; |
389 | |
390 | if (auto *V = dyn_cast<RecordVec *>(Val&: GroupsInPedantic)) |
391 | V->push_back(x: Group); |
392 | else |
393 | cast<RecordSet *>(Val&: GroupsInPedantic)->insert(V: Group); |
394 | } |
395 | } |
396 | |
397 | namespace { |
398 | enum PieceKind { |
399 | MultiPieceClass, |
400 | TextPieceClass, |
401 | PlaceholderPieceClass, |
402 | SelectPieceClass, |
403 | EnumSelectPieceClass, |
404 | PluralPieceClass, |
405 | DiffPieceClass, |
406 | SubstitutionPieceClass, |
407 | }; |
408 | |
409 | enum ModifierType { |
410 | MT_Unknown, |
411 | MT_Placeholder, |
412 | MT_Select, |
413 | MT_EnumSelect, |
414 | MT_Sub, |
415 | MT_Plural, |
416 | MT_Diff, |
417 | MT_Ordinal, |
418 | MT_Human, |
419 | MT_S, |
420 | MT_Q, |
421 | MT_ObjCClass, |
422 | MT_ObjCInstance, |
423 | MT_Quoted, |
424 | }; |
425 | |
426 | static StringRef getModifierName(ModifierType MT) { |
427 | switch (MT) { |
428 | case MT_EnumSelect: |
429 | case MT_Select: |
430 | return "select"; |
431 | case MT_Sub: |
432 | return "sub"; |
433 | case MT_Diff: |
434 | return "diff"; |
435 | case MT_Plural: |
436 | return "plural"; |
437 | case MT_Ordinal: |
438 | return "ordinal"; |
439 | case MT_Human: |
440 | return "human"; |
441 | case MT_S: |
442 | return "s"; |
443 | case MT_Q: |
444 | return "q"; |
445 | case MT_Placeholder: |
446 | return ""; |
447 | case MT_ObjCClass: |
448 | return "objcclass"; |
449 | case MT_ObjCInstance: |
450 | return "objcinstance"; |
451 | case MT_Quoted: |
452 | return "quoted"; |
453 | case MT_Unknown: |
454 | llvm_unreachable("invalid modifier type"); |
455 | } |
456 | // Unhandled case |
457 | llvm_unreachable("invalid modifier type"); |
458 | } |
459 | |
460 | struct Piece { |
461 | // This type and its derived classes are move-only. |
462 | Piece(PieceKind Kind) : ClassKind(Kind) {} |
463 | Piece(Piece const &O) = delete; |
464 | Piece &operator=(Piece const &) = delete; |
465 | virtual ~Piece() {} |
466 | |
467 | PieceKind getPieceClass() const { return ClassKind; } |
468 | static bool classof(const Piece *) { return true; } |
469 | |
470 | private: |
471 | PieceKind ClassKind; |
472 | }; |
473 | |
474 | struct MultiPiece : Piece { |
475 | MultiPiece() : Piece(MultiPieceClass) {} |
476 | MultiPiece(std::vector<Piece *> Pieces) |
477 | : Piece(MultiPieceClass), Pieces(std::move(Pieces)) {} |
478 | |
479 | std::vector<Piece *> Pieces; |
480 | |
481 | static bool classof(const Piece *P) { |
482 | return P->getPieceClass() == MultiPieceClass; |
483 | } |
484 | }; |
485 | |
486 | struct TextPiece : Piece { |
487 | StringRef Role; |
488 | std::string Text; |
489 | TextPiece(StringRef Text, StringRef Role = "") |
490 | : Piece(TextPieceClass), Role(Role), Text(Text.str()) {} |
491 | |
492 | static bool classof(const Piece *P) { |
493 | return P->getPieceClass() == TextPieceClass; |
494 | } |
495 | }; |
496 | |
497 | struct PlaceholderPiece : Piece { |
498 | ModifierType Kind; |
499 | int Index; |
500 | PlaceholderPiece(ModifierType Kind, int Index) |
501 | : Piece(PlaceholderPieceClass), Kind(Kind), Index(Index) {} |
502 | |
503 | static bool classof(const Piece *P) { |
504 | return P->getPieceClass() == PlaceholderPieceClass; |
505 | } |
506 | }; |
507 | |
508 | struct SelectPiece : Piece { |
509 | protected: |
510 | SelectPiece(PieceKind Kind, ModifierType ModKind) |
511 | : Piece(Kind), ModKind(ModKind) {} |
512 | |
513 | public: |
514 | SelectPiece(ModifierType ModKind) : SelectPiece(SelectPieceClass, ModKind) {} |
515 | |
516 | ModifierType ModKind; |
517 | std::vector<Piece *> Options; |
518 | int Index = 0; |
519 | |
520 | static bool classof(const Piece *P) { |
521 | return P->getPieceClass() == SelectPieceClass || |
522 | P->getPieceClass() == EnumSelectPieceClass || |
523 | P->getPieceClass() == PluralPieceClass; |
524 | } |
525 | }; |
526 | |
527 | struct EnumSelectPiece : SelectPiece { |
528 | EnumSelectPiece() : SelectPiece(EnumSelectPieceClass, MT_EnumSelect) {} |
529 | |
530 | StringRef EnumName; |
531 | std::vector<StringRef> OptionEnumNames; |
532 | |
533 | static bool classof(const Piece *P) { |
534 | return P->getPieceClass() == EnumSelectPieceClass; |
535 | } |
536 | }; |
537 | |
538 | struct EnumValuePiece : Piece { |
539 | ModifierType Kind; |
540 | }; |
541 | |
542 | struct PluralPiece : SelectPiece { |
543 | PluralPiece() : SelectPiece(PluralPieceClass, MT_Plural) {} |
544 | |
545 | std::vector<Piece *> OptionPrefixes; |
546 | int Index = 0; |
547 | |
548 | static bool classof(const Piece *P) { |
549 | return P->getPieceClass() == PluralPieceClass; |
550 | } |
551 | }; |
552 | |
553 | struct DiffPiece : Piece { |
554 | DiffPiece() : Piece(DiffPieceClass) {} |
555 | |
556 | Piece *Parts[4] = {}; |
557 | int Indexes[2] = {}; |
558 | |
559 | static bool classof(const Piece *P) { |
560 | return P->getPieceClass() == DiffPieceClass; |
561 | } |
562 | }; |
563 | |
564 | struct SubstitutionPiece : Piece { |
565 | SubstitutionPiece() : Piece(SubstitutionPieceClass) {} |
566 | |
567 | std::string Name; |
568 | std::vector<int> Modifiers; |
569 | |
570 | static bool classof(const Piece *P) { |
571 | return P->getPieceClass() == SubstitutionPieceClass; |
572 | } |
573 | }; |
574 | |
575 | /// Diagnostic text, parsed into pieces. |
576 | |
577 | |
578 | struct DiagnosticTextBuilder { |
579 | DiagnosticTextBuilder(DiagnosticTextBuilder const &) = delete; |
580 | DiagnosticTextBuilder &operator=(DiagnosticTextBuilder const &) = delete; |
581 | |
582 | DiagnosticTextBuilder(const RecordKeeper &Records) { |
583 | // Build up the list of substitution records. |
584 | for (auto *S : Records.getAllDerivedDefinitions(ClassName: "TextSubstitution")) { |
585 | EvaluatingRecordGuard Guard(&EvaluatingRecord, S); |
586 | Substitutions.try_emplace( |
587 | Key: S->getName(), Args: DiagText(*this, S->getValueAsString(FieldName: "Substitution"))); |
588 | } |
589 | |
590 | // Check that no diagnostic definitions have the same name as a |
591 | // substitution. |
592 | for (const Record *Diag : Records.getAllDerivedDefinitions(ClassName: "Diagnostic")) { |
593 | StringRef Name = Diag->getName(); |
594 | if (Substitutions.count(Key: Name)) |
595 | llvm::PrintFatalError( |
596 | ErrorLoc: Diag->getLoc(), |
597 | Msg: "Diagnostic '"+ Name + |
598 | "' has same name as TextSubstitution definition"); |
599 | } |
600 | } |
601 | |
602 | std::vector<std::string> buildForDocumentation(StringRef Role, |
603 | const Record *R); |
604 | std::string buildForDefinition(const Record *R); |
605 | llvm::SmallVector<std::pair< |
606 | std::string, llvm::SmallVector<std::pair<unsigned, std::string>>>> |
607 | buildForEnum(const Record *R); |
608 | |
609 | Piece *getSubstitution(SubstitutionPiece *S) const { |
610 | auto It = Substitutions.find(Key: S->Name); |
611 | if (It == Substitutions.end()) |
612 | llvm::PrintFatalError(Msg: "Failed to find substitution with name: "+ |
613 | S->Name); |
614 | return It->second.Root; |
615 | } |
616 | |
617 | [[noreturn]] void PrintFatalError(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(EnumSelect); |
737 | CASE(Plural); |
738 | CASE(Diff); |
739 | CASE(Substitution); |
740 | #undef CASE |
741 | } |
742 | } |
743 | |
744 | void VisitSubstitution(SubstitutionPiece *P) { |
745 | SubstitutionContext Guard(*this, P); |
746 | Visit(P: Guard.Substitution); |
747 | } |
748 | |
749 | int mapIndex(int Idx, |
750 | ModifierMappingsType const &ModifierMappings) const { |
751 | if (!ModifierMappings) |
752 | return Idx; |
753 | if (ModifierMappings->size() <= static_cast<unsigned>(Idx)) |
754 | Builder.PrintFatalError(Msg: "Modifier value '"+ std::to_string(val: Idx) + |
755 | "' is not valid for this mapping (has "+ |
756 | std::to_string(val: ModifierMappings->size()) + |
757 | " mappings)"); |
758 | return (*ModifierMappings)[Idx]; |
759 | } |
760 | |
761 | int mapIndex(int Idx) const { |
762 | return mapIndex(Idx, ModifierMappings); |
763 | } |
764 | |
765 | protected: |
766 | DiagnosticTextBuilder &Builder; |
767 | ModifierMappingsType ModifierMappings; |
768 | }; |
769 | |
770 | void escapeRST(StringRef Str, std::string &Out) { |
771 | for (auto K : Str) { |
772 | if (StringRef("`*|_[]\\").count(C: K)) |
773 | Out.push_back(c: '\\'); |
774 | Out.push_back(c: K); |
775 | } |
776 | } |
777 | |
778 | template <typename It> void padToSameLength(It Begin, It End) { |
779 | size_t Width = 0; |
780 | for (It I = Begin; I != End; ++I) |
781 | Width = std::max(Width, I->size()); |
782 | for (It I = Begin; I != End; ++I) |
783 | (*I) += std::string(Width - I->size(), ' '); |
784 | } |
785 | |
786 | template <typename It> void makeTableRows(It Begin, It End) { |
787 | if (Begin == End) |
788 | return; |
789 | padToSameLength(Begin, End); |
790 | for (It I = Begin; I != End; ++I) |
791 | *I = "|"+ *I + "|"; |
792 | } |
793 | |
794 | void makeRowSeparator(std::string &Str) { |
795 | for (char &K : Str) |
796 | K = (K == '|' ? '+' : '-'); |
797 | } |
798 | |
799 | struct DiagTextDocPrinter : DiagTextVisitor<DiagTextDocPrinter> { |
800 | using BaseTy = DiagTextVisitor<DiagTextDocPrinter>; |
801 | DiagTextDocPrinter(DiagnosticTextBuilder &Builder, |
802 | std::vector<std::string> &RST) |
803 | : BaseTy(Builder), RST(RST) {} |
804 | |
805 | void gatherNodes( |
806 | Piece *OrigP, const ModifierMappingsType &CurrentMappings, |
807 | std::vector<std::pair<Piece *, ModifierMappingsType>> &Pieces) const { |
808 | if (auto *Sub = dyn_cast<SubstitutionPiece>(Val: OrigP)) { |
809 | ModifierMappingsType NewMappings = |
810 | getSubstitutionMappings(P: Sub, Mappings: CurrentMappings); |
811 | return gatherNodes(OrigP: Builder.getSubstitution(S: Sub), CurrentMappings: NewMappings, Pieces); |
812 | } |
813 | if (auto *MD = dyn_cast<MultiPiece>(Val: OrigP)) { |
814 | for (Piece *Node : MD->Pieces) |
815 | gatherNodes(OrigP: Node, CurrentMappings, Pieces); |
816 | return; |
817 | } |
818 | Pieces.push_back(x: std::make_pair(x&: OrigP, y: CurrentMappings)); |
819 | } |
820 | |
821 | void VisitMulti(MultiPiece *P) { |
822 | if (P->Pieces.empty()) { |
823 | RST.push_back(x: ""); |
824 | return; |
825 | } |
826 | |
827 | if (P->Pieces.size() == 1) |
828 | return Visit(P: P->Pieces[0]); |
829 | |
830 | // Flatten the list of nodes, replacing any substitution pieces with the |
831 | // recursively flattened substituted node. |
832 | std::vector<std::pair<Piece *, ModifierMappingsType>> Pieces; |
833 | gatherNodes(OrigP: P, CurrentMappings: ModifierMappings, Pieces); |
834 | |
835 | std::string EmptyLinePrefix; |
836 | size_t Start = RST.size(); |
837 | bool HasMultipleLines = true; |
838 | for (const std::pair<Piece *, ModifierMappingsType> &NodePair : Pieces) { |
839 | std::vector<std::string> Lines; |
840 | DiagTextDocPrinter Visitor{Builder, Lines}; |
841 | Visitor.ModifierMappings = NodePair.second; |
842 | Visitor.Visit(P: NodePair.first); |
843 | |
844 | if (Lines.empty()) |
845 | continue; |
846 | |
847 | // We need a vertical separator if either this or the previous piece is a |
848 | // multi-line piece, or this is the last piece. |
849 | const char *Separator = (Lines.size() > 1 || HasMultipleLines) ? "|": ""; |
850 | HasMultipleLines = Lines.size() > 1; |
851 | |
852 | if (Start + Lines.size() > RST.size()) |
853 | RST.resize(new_size: Start + Lines.size(), x: EmptyLinePrefix); |
854 | |
855 | padToSameLength(Begin: Lines.begin(), End: Lines.end()); |
856 | for (size_t I = 0; I != Lines.size(); ++I) |
857 | RST[Start + I] += Separator + Lines[I]; |
858 | std::string Empty(Lines[0].size(), ' '); |
859 | for (size_t I = Start + Lines.size(); I != RST.size(); ++I) |
860 | RST[I] += Separator + Empty; |
861 | EmptyLinePrefix += Separator + Empty; |
862 | } |
863 | for (size_t I = Start; I != RST.size(); ++I) |
864 | RST[I] += "|"; |
865 | EmptyLinePrefix += "|"; |
866 | |
867 | makeRowSeparator(Str&: EmptyLinePrefix); |
868 | RST.insert(position: RST.begin() + Start, x: EmptyLinePrefix); |
869 | RST.insert(position: RST.end(), x: EmptyLinePrefix); |
870 | } |
871 | |
872 | void VisitText(TextPiece *P) { |
873 | RST.push_back(x: ""); |
874 | auto &S = RST.back(); |
875 | |
876 | StringRef T = P->Text; |
877 | while (T.consume_front(Prefix: " ")) |
878 | RST.back() += " |nbsp| "; |
879 | |
880 | std::string Suffix; |
881 | while (T.consume_back(Suffix: " ")) |
882 | Suffix += " |nbsp| "; |
883 | |
884 | if (!T.empty()) { |
885 | S += ':'; |
886 | S += P->Role; |
887 | S += ":`"; |
888 | escapeRST(Str: T, Out&: S); |
889 | S += '`'; |
890 | } |
891 | |
892 | S += Suffix; |
893 | } |
894 | |
895 | void VisitPlaceholder(PlaceholderPiece *P) { |
896 | RST.push_back(x: std::string(":placeholder:`") + |
897 | char('A' + mapIndex(Idx: P->Index)) + "`"); |
898 | } |
899 | |
900 | void VisitSelect(SelectPiece *P) { |
901 | std::vector<size_t> SeparatorIndexes; |
902 | SeparatorIndexes.push_back(x: RST.size()); |
903 | RST.emplace_back(); |
904 | for (auto *O : P->Options) { |
905 | Visit(P: O); |
906 | SeparatorIndexes.push_back(x: RST.size()); |
907 | RST.emplace_back(); |
908 | } |
909 | |
910 | makeTableRows(Begin: RST.begin() + SeparatorIndexes.front(), |
911 | End: RST.begin() + SeparatorIndexes.back() + 1); |
912 | for (size_t I : SeparatorIndexes) |
913 | makeRowSeparator(Str&: RST[I]); |
914 | } |
915 | |
916 | void VisitEnumSelect(EnumSelectPiece *P) { |
917 | // Document this as if it were a 'select', which properly prints all of the |
918 | // options correctly in a readable/reasonable manner. There isn't really |
919 | // anything valuable we could add to readers here. |
920 | VisitSelect(P); |
921 | } |
922 | |
923 | void VisitPlural(PluralPiece *P) { VisitSelect(P); } |
924 | |
925 | void VisitDiff(DiffPiece *P) { |
926 | // Render %diff{a $ b $ c|d}e,f as %select{a %e b %f c|d}. |
927 | PlaceholderPiece E(MT_Placeholder, P->Indexes[0]); |
928 | PlaceholderPiece F(MT_Placeholder, P->Indexes[1]); |
929 | |
930 | MultiPiece FirstOption; |
931 | FirstOption.Pieces.push_back(x: P->Parts[0]); |
932 | FirstOption.Pieces.push_back(x: &E); |
933 | FirstOption.Pieces.push_back(x: P->Parts[1]); |
934 | FirstOption.Pieces.push_back(x: &F); |
935 | FirstOption.Pieces.push_back(x: P->Parts[2]); |
936 | |
937 | SelectPiece Select(MT_Diff); |
938 | Select.Options.push_back(x: &FirstOption); |
939 | Select.Options.push_back(x: P->Parts[3]); |
940 | |
941 | VisitSelect(P: &Select); |
942 | } |
943 | |
944 | std::vector<std::string> &RST; |
945 | }; |
946 | |
947 | struct DiagEnumPrinter : DiagTextVisitor<DiagEnumPrinter> { |
948 | public: |
949 | using BaseTy = DiagTextVisitor<DiagEnumPrinter>; |
950 | using EnumeratorItem = std::pair<unsigned, std::string>; |
951 | using EnumeratorList = llvm::SmallVector<EnumeratorItem>; |
952 | using ResultTy = llvm::SmallVector<std::pair<std::string, EnumeratorList>>; |
953 | |
954 | DiagEnumPrinter(DiagnosticTextBuilder &Builder, ResultTy &Result) |
955 | : BaseTy(Builder), Result(Result) {} |
956 | |
957 | ResultTy &Result; |
958 | |
959 | void VisitMulti(MultiPiece *P) { |
960 | for (auto *Child : P->Pieces) |
961 | Visit(P: Child); |
962 | } |
963 | void VisitText(TextPiece *P) {} |
964 | void VisitPlaceholder(PlaceholderPiece *P) {} |
965 | void VisitDiff(DiffPiece *P) {} |
966 | void VisitSelect(SelectPiece *P) { |
967 | for (auto *D : P->Options) |
968 | Visit(P: D); |
969 | } |
970 | void VisitPlural(PluralPiece *P) { VisitSelect(P); } |
971 | void VisitEnumSelect(EnumSelectPiece *P) { |
972 | assert(P->Options.size() == P->OptionEnumNames.size()); |
973 | |
974 | if (!P->EnumName.empty()) { |
975 | EnumeratorList List; |
976 | |
977 | for (const auto &Tup : llvm::enumerate(First&: P->OptionEnumNames)) |
978 | if (!Tup.value().empty()) |
979 | List.emplace_back(Args: Tup.index(), Args&: Tup.value()); |
980 | |
981 | Result.emplace_back(Args&: P->EnumName, Args&: List); |
982 | } |
983 | |
984 | VisitSelect(P); |
985 | } |
986 | }; |
987 | |
988 | struct DiagTextPrinter : DiagTextVisitor<DiagTextPrinter> { |
989 | public: |
990 | using BaseTy = DiagTextVisitor<DiagTextPrinter>; |
991 | DiagTextPrinter(DiagnosticTextBuilder &Builder, std::string &Result) |
992 | : BaseTy(Builder), Result(Result) {} |
993 | |
994 | void VisitMulti(MultiPiece *P) { |
995 | for (auto *Child : P->Pieces) |
996 | Visit(P: Child); |
997 | } |
998 | void VisitText(TextPiece *P) { Result += P->Text; } |
999 | void VisitPlaceholder(PlaceholderPiece *P) { |
1000 | Result += "%"; |
1001 | Result += getModifierName(MT: P->Kind); |
1002 | addInt(Val: mapIndex(Idx: P->Index)); |
1003 | } |
1004 | void VisitSelect(SelectPiece *P) { |
1005 | Result += "%"; |
1006 | Result += getModifierName(MT: P->ModKind); |
1007 | if (P->ModKind == MT_Select || P->ModKind == MT_EnumSelect) { |
1008 | Result += "{"; |
1009 | for (auto *D : P->Options) { |
1010 | Visit(P: D); |
1011 | Result += '|'; |
1012 | } |
1013 | if (!P->Options.empty()) |
1014 | Result.erase(position: --Result.end()); |
1015 | Result += '}'; |
1016 | } |
1017 | addInt(Val: mapIndex(Idx: P->Index)); |
1018 | } |
1019 | |
1020 | void VisitPlural(PluralPiece *P) { |
1021 | Result += "%plural{"; |
1022 | assert(P->Options.size() == P->OptionPrefixes.size()); |
1023 | for (const auto [Prefix, Option] : |
1024 | zip_equal(t&: P->OptionPrefixes, u&: P->Options)) { |
1025 | if (Prefix) |
1026 | Visit(P: Prefix); |
1027 | Visit(P: Option); |
1028 | Result += "|"; |
1029 | } |
1030 | if (!P->Options.empty()) |
1031 | Result.erase(position: --Result.end()); |
1032 | Result += '}'; |
1033 | addInt(Val: mapIndex(Idx: P->Index)); |
1034 | } |
1035 | |
1036 | void VisitEnumSelect(EnumSelectPiece *P) { |
1037 | // Print as if we are a 'select', which will result in the compiler just |
1038 | // treating this like a normal select. This way we don't have to do any |
1039 | // special work for the compiler to consume these. |
1040 | VisitSelect(P); |
1041 | } |
1042 | |
1043 | void VisitDiff(DiffPiece *P) { |
1044 | Result += "%diff{"; |
1045 | Visit(P: P->Parts[0]); |
1046 | Result += "$"; |
1047 | Visit(P: P->Parts[1]); |
1048 | Result += "$"; |
1049 | Visit(P: P->Parts[2]); |
1050 | Result += "|"; |
1051 | Visit(P: P->Parts[3]); |
1052 | Result += "}"; |
1053 | addInt(Val: mapIndex(Idx: P->Indexes[0])); |
1054 | Result += ","; |
1055 | addInt(Val: mapIndex(Idx: P->Indexes[1])); |
1056 | } |
1057 | |
1058 | void addInt(int Val) { Result += std::to_string(val: Val); } |
1059 | |
1060 | std::string &Result; |
1061 | }; |
1062 | |
1063 | int DiagnosticTextBuilder::DiagText::parseModifier(StringRef &Text) const { |
1064 | if (Text.empty() || !isdigit(Text[0])) |
1065 | Builder.PrintFatalError(Msg: "expected modifier in diagnostic"); |
1066 | int Val = 0; |
1067 | do { |
1068 | Val *= 10; |
1069 | Val += Text[0] - '0'; |
1070 | Text = Text.drop_front(); |
1071 | } while (!Text.empty() && isdigit(Text[0])); |
1072 | return Val; |
1073 | } |
1074 | |
1075 | Piece *DiagnosticTextBuilder::DiagText::parseDiagText(StringRef &Text, |
1076 | StopAt Stop) { |
1077 | std::vector<Piece *> Parsed; |
1078 | |
1079 | constexpr StringLiteral StopSets[] = {"%", "%|}", "%|}$"}; |
1080 | StringRef StopSet = StopSets[static_cast<int>(Stop)]; |
1081 | |
1082 | while (!Text.empty()) { |
1083 | size_t End = (size_t)-2; |
1084 | do |
1085 | End = Text.find_first_of(Chars: StopSet, From: End + 2); |
1086 | while ( |
1087 | End < Text.size() - 1 && Text[End] == '%' && |
1088 | (Text[End + 1] == '%' || Text[End + 1] == '|' || Text[End + 1] == '$')); |
1089 | |
1090 | if (End) { |
1091 | Parsed.push_back(x: New<TextPiece>(args: Text.slice(Start: 0, End), args: "diagtext")); |
1092 | Text = Text.substr(Start: End); |
1093 | if (Text.empty()) |
1094 | break; |
1095 | } |
1096 | |
1097 | if (Text[0] == '|' || Text[0] == '}' || Text[0] == '$') |
1098 | break; |
1099 | |
1100 | // Drop the '%'. |
1101 | Text = Text.drop_front(); |
1102 | |
1103 | // Extract the (optional) modifier. |
1104 | size_t ModLength = Text.find_first_of(Chars: "0123456789<{"); |
1105 | StringRef Modifier = Text.slice(Start: 0, End: ModLength); |
1106 | Text = Text.substr(Start: ModLength); |
1107 | ModifierType ModType = StringSwitch<ModifierType>{Modifier} |
1108 | .Case(S: "select", Value: MT_Select) |
1109 | .Case(S: "enum_select", Value: MT_EnumSelect) |
1110 | .Case(S: "sub", Value: MT_Sub) |
1111 | .Case(S: "diff", Value: MT_Diff) |
1112 | .Case(S: "plural", Value: MT_Plural) |
1113 | .Case(S: "s", Value: MT_S) |
1114 | .Case(S: "ordinal", Value: MT_Ordinal) |
1115 | .Case(S: "human", Value: MT_Human) |
1116 | .Case(S: "q", Value: MT_Q) |
1117 | .Case(S: "objcclass", Value: MT_ObjCClass) |
1118 | .Case(S: "objcinstance", Value: MT_ObjCInstance) |
1119 | .Case(S: "quoted", Value: MT_Quoted) |
1120 | .Case(S: "", Value: MT_Placeholder) |
1121 | .Default(Value: MT_Unknown); |
1122 | |
1123 | auto ExpectAndConsume = [&](StringRef Prefix) { |
1124 | if (!Text.consume_front(Prefix)) |
1125 | Builder.PrintFatalError(Msg: "expected '"+ Prefix + "' while parsing %"+ |
1126 | Modifier); |
1127 | }; |
1128 | |
1129 | if (ModType != MT_EnumSelect && Text[0] == '<') |
1130 | Builder.PrintFatalError(Msg: "modifier '<' syntax not valid with %"+ |
1131 | Modifier); |
1132 | |
1133 | switch (ModType) { |
1134 | case MT_Unknown: |
1135 | Builder.PrintFatalError(Msg: "Unknown modifier type: "+ Modifier); |
1136 | case MT_Select: { |
1137 | SelectPiece *Select = New<SelectPiece>(args: MT_Select); |
1138 | do { |
1139 | Text = Text.drop_front(); // '{' or '|' |
1140 | Select->Options.push_back( |
1141 | x: parseDiagText(Text, Stop: StopAt::PipeOrCloseBrace)); |
1142 | assert(!Text.empty() && "malformed %select"); |
1143 | } while (Text.front() == '|'); |
1144 | ExpectAndConsume("}"); |
1145 | Select->Index = parseModifier(Text); |
1146 | Parsed.push_back(x: Select); |
1147 | continue; |
1148 | } |
1149 | case MT_EnumSelect: { |
1150 | EnumSelectPiece *EnumSelect = New<EnumSelectPiece>(); |
1151 | if (Text[0] != '<') |
1152 | Builder.PrintFatalError(Msg: "expected '<' after "+ Modifier); |
1153 | |
1154 | Text = Text.drop_front(); // Drop '<' |
1155 | size_t EnumNameLen = Text.find_first_of(C: '>'); |
1156 | EnumSelect->EnumName = Text.slice(Start: 0, End: EnumNameLen); |
1157 | Text = Text.substr(Start: EnumNameLen); |
1158 | ExpectAndConsume(">"); |
1159 | |
1160 | if (Text[0] != '{') |
1161 | Builder.PrintFatalError(Msg: "expected '{' after "+ Modifier); |
1162 | |
1163 | do { |
1164 | Text = Text.drop_front(); // '{' or '|' |
1165 | |
1166 | bool BracketsRequired = false; |
1167 | if (Text[0] == '%') { |
1168 | BracketsRequired = true; |
1169 | Text = Text.drop_front(); // '%' |
1170 | size_t OptionNameLen = Text.find_first_of(Chars: "{"); |
1171 | EnumSelect->OptionEnumNames.push_back(x: Text.slice(Start: 0, End: OptionNameLen)); |
1172 | Text = Text.substr(Start: OptionNameLen); |
1173 | } else { |
1174 | EnumSelect->OptionEnumNames.push_back(x: {}); |
1175 | } |
1176 | |
1177 | if (BracketsRequired) |
1178 | ExpectAndConsume("{"); |
1179 | else if (Text.front() == '{') { |
1180 | Text = Text.drop_front(); |
1181 | BracketsRequired = true; |
1182 | } |
1183 | |
1184 | EnumSelect->Options.push_back( |
1185 | x: parseDiagText(Text, Stop: StopAt::PipeOrCloseBrace)); |
1186 | |
1187 | if (BracketsRequired) |
1188 | ExpectAndConsume("}"); |
1189 | |
1190 | assert(!Text.empty() && "malformed %select"); |
1191 | } while (Text.front() == '|'); |
1192 | |
1193 | ExpectAndConsume("}"); |
1194 | EnumSelect->Index = parseModifier(Text); |
1195 | Parsed.push_back(x: EnumSelect); |
1196 | continue; |
1197 | } |
1198 | case MT_Plural: { |
1199 | PluralPiece *Plural = New<PluralPiece>(); |
1200 | do { |
1201 | Text = Text.drop_front(); // '{' or '|' |
1202 | size_t End = Text.find_first_of(C: ':'); |
1203 | if (End == StringRef::npos) |
1204 | Builder.PrintFatalError(Msg: "expected ':' while parsing %plural"); |
1205 | ++End; |
1206 | assert(!Text.empty()); |
1207 | Plural->OptionPrefixes.push_back( |
1208 | x: New<TextPiece>(args: Text.slice(Start: 0, End), args: "diagtext")); |
1209 | Text = Text.substr(Start: End); |
1210 | Plural->Options.push_back( |
1211 | x: parseDiagText(Text, Stop: StopAt::PipeOrCloseBrace)); |
1212 | assert(!Text.empty() && "malformed %plural"); |
1213 | } while (Text.front() == '|'); |
1214 | ExpectAndConsume("}"); |
1215 | Plural->Index = parseModifier(Text); |
1216 | Parsed.push_back(x: Plural); |
1217 | continue; |
1218 | } |
1219 | case MT_Sub: { |
1220 | SubstitutionPiece *Sub = New<SubstitutionPiece>(); |
1221 | ExpectAndConsume("{"); |
1222 | size_t NameSize = Text.find_first_of(C: '}'); |
1223 | assert(NameSize != size_t(-1) && "failed to find the end of the name"); |
1224 | assert(NameSize != 0 && "empty name?"); |
1225 | Sub->Name = Text.substr(Start: 0, N: NameSize).str(); |
1226 | Text = Text.drop_front(N: NameSize); |
1227 | ExpectAndConsume("}"); |
1228 | if (!Text.empty()) { |
1229 | while (true) { |
1230 | if (!isdigit(Text[0])) |
1231 | break; |
1232 | Sub->Modifiers.push_back(x: parseModifier(Text)); |
1233 | if (!Text.consume_front(Prefix: ",")) |
1234 | break; |
1235 | assert(!Text.empty() && isdigit(Text[0]) && |
1236 | "expected another modifier"); |
1237 | } |
1238 | } |
1239 | Parsed.push_back(x: Sub); |
1240 | continue; |
1241 | } |
1242 | case MT_Diff: { |
1243 | DiffPiece *Diff = New<DiffPiece>(); |
1244 | ExpectAndConsume("{"); |
1245 | Diff->Parts[0] = parseDiagText(Text, Stop: StopAt::Dollar); |
1246 | ExpectAndConsume("$"); |
1247 | Diff->Parts[1] = parseDiagText(Text, Stop: StopAt::Dollar); |
1248 | ExpectAndConsume("$"); |
1249 | Diff->Parts[2] = parseDiagText(Text, Stop: StopAt::PipeOrCloseBrace); |
1250 | ExpectAndConsume("|"); |
1251 | Diff->Parts[3] = parseDiagText(Text, Stop: StopAt::PipeOrCloseBrace); |
1252 | ExpectAndConsume("}"); |
1253 | Diff->Indexes[0] = parseModifier(Text); |
1254 | ExpectAndConsume(","); |
1255 | Diff->Indexes[1] = parseModifier(Text); |
1256 | Parsed.push_back(x: Diff); |
1257 | continue; |
1258 | } |
1259 | case MT_S: { |
1260 | SelectPiece *Select = New<SelectPiece>(args&: ModType); |
1261 | Select->Options.push_back(x: New<TextPiece>(args: "")); |
1262 | Select->Options.push_back(x: New<TextPiece>(args: "s", args: "diagtext")); |
1263 | Select->Index = parseModifier(Text); |
1264 | Parsed.push_back(x: Select); |
1265 | continue; |
1266 | } |
1267 | case MT_Q: |
1268 | case MT_Placeholder: |
1269 | case MT_ObjCClass: |
1270 | case MT_ObjCInstance: |
1271 | case MT_Quoted: |
1272 | case MT_Ordinal: |
1273 | case MT_Human: { |
1274 | Parsed.push_back(x: New<PlaceholderPiece>(args&: ModType, args: parseModifier(Text))); |
1275 | continue; |
1276 | } |
1277 | } |
1278 | } |
1279 | |
1280 | return New<MultiPiece>(args&: Parsed); |
1281 | } |
1282 | |
1283 | std::vector<std::string> |
1284 | DiagnosticTextBuilder::buildForDocumentation(StringRef Severity, |
1285 | const Record *R) { |
1286 | EvaluatingRecordGuard Guard(&EvaluatingRecord, R); |
1287 | StringRef Text = R->getValueAsString(FieldName: "Summary"); |
1288 | |
1289 | DiagText D(*this, Text); |
1290 | TextPiece *Prefix = D.New<TextPiece>(args&: Severity, args&: Severity); |
1291 | Prefix->Text += ": "; |
1292 | auto *MP = dyn_cast<MultiPiece>(Val: D.Root); |
1293 | if (!MP) { |
1294 | MP = D.New<MultiPiece>(); |
1295 | MP->Pieces.push_back(x: D.Root); |
1296 | D.Root = MP; |
1297 | } |
1298 | MP->Pieces.insert(position: MP->Pieces.begin(), x: Prefix); |
1299 | std::vector<std::string> Result; |
1300 | DiagTextDocPrinter{*this, Result}.Visit(P: D.Root); |
1301 | return Result; |
1302 | } |
1303 | |
1304 | DiagEnumPrinter::ResultTy DiagnosticTextBuilder::buildForEnum(const Record *R) { |
1305 | EvaluatingRecordGuard Guard(&EvaluatingRecord, R); |
1306 | StringRef Text = R->getValueAsString(FieldName: "Summary"); |
1307 | DiagText D(*this, Text); |
1308 | DiagEnumPrinter::ResultTy Result; |
1309 | DiagEnumPrinter{*this, Result}.Visit(P: D.Root); |
1310 | return Result; |
1311 | } |
1312 | |
1313 | std::string DiagnosticTextBuilder::buildForDefinition(const Record *R) { |
1314 | EvaluatingRecordGuard Guard(&EvaluatingRecord, R); |
1315 | StringRef Text = R->getValueAsString(FieldName: "Summary"); |
1316 | DiagText D(*this, Text); |
1317 | std::string Result; |
1318 | DiagTextPrinter{*this, Result}.Visit(P: D.Root); |
1319 | return Result; |
1320 | } |
1321 | |
1322 | } // namespace |
1323 | |
1324 | //===----------------------------------------------------------------------===// |
1325 | // Warning Tables (.inc file) generation. |
1326 | //===----------------------------------------------------------------------===// |
1327 | |
1328 | static bool isError(const Record &Diag) { |
1329 | return Diag.getValueAsDef(FieldName: "Class")->getName() == "CLASS_ERROR"; |
1330 | } |
1331 | |
1332 | static bool isRemark(const Record &Diag) { |
1333 | return Diag.getValueAsDef(FieldName: "Class")->getName() == "CLASS_REMARK"; |
1334 | } |
1335 | |
1336 | // Presumes the text has been split at the first whitespace or hyphen. |
1337 | static bool isExemptAtStart(StringRef Text) { |
1338 | // Fast path, the first character is lowercase or not alphanumeric. |
1339 | if (Text.empty() || isLower(C: Text[0]) || !isAlnum(C: Text[0])) |
1340 | return true; |
1341 | |
1342 | // If the text is all uppercase (or numbers, +, or _), then we assume it's an |
1343 | // acronym and that's allowed. This covers cases like ISO, C23, C++14, and |
1344 | // OBJECT_MODE. However, if there's only a single letter other than "C", we |
1345 | // do not exempt it so that we catch a case like "A really bad idea" while |
1346 | // still allowing a case like "C does not allow...". |
1347 | if (all_of(Range&: Text, P: [](char C) { |
1348 | return isUpper(C) || isDigit(C) || C == '+' || C == '_'; |
1349 | })) |
1350 | return Text.size() > 1 || Text[0] == 'C'; |
1351 | |
1352 | // Otherwise, there are a few other exemptions. |
1353 | return StringSwitch<bool>(Text) |
1354 | .Case(S: "AddressSanitizer", Value: true) |
1355 | .Case(S: "CFString", Value: true) |
1356 | .Case(S: "Clang", Value: true) |
1357 | .Case(S: "Fuchsia", Value: true) |
1358 | .Case(S: "GNUstep", Value: true) |
1359 | .Case(S: "IBOutletCollection", Value: true) |
1360 | .Case(S: "Microsoft", Value: true) |
1361 | .Case(S: "Neon", Value: true) |
1362 | .StartsWith(S: "NSInvocation", Value: true) // NSInvocation, NSInvocation's |
1363 | .Case(S: "Objective", Value: true) // Objective-C (hyphen is a word boundary) |
1364 | .Case(S: "OpenACC", Value: true) |
1365 | .Case(S: "OpenCL", Value: true) |
1366 | .Case(S: "OpenMP", Value: true) |
1367 | .Case(S: "Pascal", Value: true) |
1368 | .Case(S: "Swift", Value: true) |
1369 | .Case(S: "Unicode", Value: true) |
1370 | .Case(S: "Vulkan", Value: true) |
1371 | .Case(S: "WebAssembly", Value: true) |
1372 | .Default(Value: false); |
1373 | } |
1374 | |
1375 | // Does not presume the text has been split at all. |
1376 | static bool isExemptAtEnd(StringRef Text) { |
1377 | // Rather than come up with a list of characters that are allowed, we go the |
1378 | // other way and look only for characters that are not allowed. |
1379 | switch (Text.back()) { |
1380 | default: |
1381 | return true; |
1382 | case '?': |
1383 | // Explicitly allowed to support "; did you mean?". |
1384 | return true; |
1385 | case '.': |
1386 | case '!': |
1387 | return false; |
1388 | } |
1389 | } |
1390 | |
1391 | static void verifyDiagnosticWording(const Record &Diag) { |
1392 | StringRef FullDiagText = Diag.getValueAsString(FieldName: "Summary"); |
1393 | |
1394 | auto DiagnoseStart = [&](StringRef Text) { |
1395 | // Verify that the text does not start with a capital letter, except for |
1396 | // special cases that are exempt like ISO and C++. Find the first word |
1397 | // by looking for a word breaking character. |
1398 | char Separators[] = {' ', '-', ',', '}'}; |
1399 | auto Iter = std::find_first_of( |
1400 | first1: Text.begin(), last1: Text.end(), first2: std::begin(arr&: Separators), last2: std::end(arr&: Separators)); |
1401 | |
1402 | StringRef First = Text.substr(Start: 0, N: Iter - Text.begin()); |
1403 | if (!isExemptAtStart(Text: First)) { |
1404 | PrintError(Rec: &Diag, |
1405 | Msg: "Diagnostics should not start with a capital letter; '"+ |
1406 | First + "' is invalid"); |
1407 | } |
1408 | }; |
1409 | |
1410 | auto DiagnoseEnd = [&](StringRef Text) { |
1411 | // Verify that the text does not end with punctuation like '.' or '!'. |
1412 | if (!isExemptAtEnd(Text)) { |
1413 | PrintError(Rec: &Diag, Msg: "Diagnostics should not end with punctuation; '"+ |
1414 | Text.substr(Start: Text.size() - 1, N: 1) + "' is invalid"); |
1415 | } |
1416 | }; |
1417 | |
1418 | // If the diagnostic starts with %select, look through it to see whether any |
1419 | // of the options will cause a problem. |
1420 | if (FullDiagText.starts_with(Prefix: "%select{")) { |
1421 | // Do a balanced delimiter scan from the start of the text to find the |
1422 | // closing '}', skipping intermediary {} pairs. |
1423 | |
1424 | size_t BraceCount = 1; |
1425 | constexpr size_t PercentSelectBraceLen = sizeof("%select{") - 1; |
1426 | auto Iter = FullDiagText.begin() + PercentSelectBraceLen; |
1427 | for (auto End = FullDiagText.end(); Iter != End; ++Iter) { |
1428 | char Ch = *Iter; |
1429 | if (Ch == '{') |
1430 | ++BraceCount; |
1431 | else if (Ch == '}') |
1432 | --BraceCount; |
1433 | if (!BraceCount) |
1434 | break; |
1435 | } |
1436 | // Defending against a malformed diagnostic string. |
1437 | if (BraceCount != 0) |
1438 | return; |
1439 | |
1440 | StringRef SelectText = |
1441 | FullDiagText.substr(Start: PercentSelectBraceLen, N: Iter - FullDiagText.begin() - |
1442 | PercentSelectBraceLen); |
1443 | SmallVector<StringRef, 4> SelectPieces; |
1444 | SelectText.split(A&: SelectPieces, Separator: '|'); |
1445 | |
1446 | // Walk over all of the individual pieces of select text to see if any of |
1447 | // them start with an invalid character. If any of the select pieces is |
1448 | // empty, we need to look at the first word after the %select to see |
1449 | // whether that is invalid or not. If all of the pieces are fine, then we |
1450 | // don't need to check anything else about the start of the diagnostic. |
1451 | bool CheckSecondWord = false; |
1452 | for (StringRef Piece : SelectPieces) { |
1453 | if (Piece.empty()) |
1454 | CheckSecondWord = true; |
1455 | else |
1456 | DiagnoseStart(Piece); |
1457 | } |
1458 | |
1459 | if (CheckSecondWord) { |
1460 | // There was an empty select piece, so we need to check the second |
1461 | // word. This catches situations like '%select{|fine}0 Not okay'. Add |
1462 | // two to account for the closing curly brace and the number after it. |
1463 | StringRef AfterSelect = |
1464 | FullDiagText.substr(Start: Iter - FullDiagText.begin() + 2).ltrim(); |
1465 | DiagnoseStart(AfterSelect); |
1466 | } |
1467 | } else { |
1468 | // If the start of the diagnostic is not %select, we can check the first |
1469 | // word and be done with it. |
1470 | DiagnoseStart(FullDiagText); |
1471 | } |
1472 | |
1473 | // If the last character in the diagnostic is a number preceded by a }, scan |
1474 | // backwards to see if this is for a %select{...}0. If it is, we need to look |
1475 | // at each piece to see whether it ends in punctuation or not. |
1476 | bool StillNeedToDiagEnd = true; |
1477 | if (isDigit(C: FullDiagText.back()) && *(FullDiagText.end() - 2) == '}') { |
1478 | // Scan backwards to find the opening curly brace. |
1479 | size_t BraceCount = 1; |
1480 | auto Iter = FullDiagText.end() - sizeof("}0"); |
1481 | for (auto End = FullDiagText.begin(); Iter != End; --Iter) { |
1482 | char Ch = *Iter; |
1483 | if (Ch == '}') |
1484 | ++BraceCount; |
1485 | else if (Ch == '{') |
1486 | --BraceCount; |
1487 | if (!BraceCount) |
1488 | break; |
1489 | } |
1490 | // Defending against a malformed diagnostic string. |
1491 | if (BraceCount != 0) |
1492 | return; |
1493 | |
1494 | // Continue the backwards scan to find the word before the '{' to see if it |
1495 | // is 'select'. |
1496 | constexpr size_t SelectLen = sizeof("select") - 1; |
1497 | bool IsSelect = |
1498 | (FullDiagText.substr(Start: Iter - SelectLen - FullDiagText.begin(), |
1499 | N: SelectLen) == "select"); |
1500 | if (IsSelect) { |
1501 | // Gather the content between the {} for the select in question so we can |
1502 | // split it into pieces. |
1503 | StillNeedToDiagEnd = false; // No longer need to handle the end. |
1504 | StringRef SelectText = |
1505 | FullDiagText.substr(Start: Iter - FullDiagText.begin() + /*{*/ 1, |
1506 | N: FullDiagText.end() - Iter - /*pos before }0*/ 3); |
1507 | SmallVector<StringRef, 4> SelectPieces; |
1508 | SelectText.split(A&: SelectPieces, Separator: '|'); |
1509 | for (StringRef Piece : SelectPieces) { |
1510 | // Not worrying about a situation like: "this is bar. %select{foo|}0". |
1511 | if (!Piece.empty()) |
1512 | DiagnoseEnd(Piece); |
1513 | } |
1514 | } |
1515 | } |
1516 | |
1517 | // If we didn't already cover the diagnostic because of a %select, handle it |
1518 | // now. |
1519 | if (StillNeedToDiagEnd) |
1520 | DiagnoseEnd(FullDiagText); |
1521 | |
1522 | // FIXME: This could also be improved by looking for instances of clang or |
1523 | // gcc in the diagnostic and recommend Clang or GCC instead. However, this |
1524 | // runs into odd situations like [[clang::warn_unused_result]], |
1525 | // #pragma clang, or --unwindlib=libgcc. |
1526 | } |
1527 | |
1528 | /// ClangDiagsCompatIDsEmitter - Emit a set of 'compatibility diagnostic ids' |
1529 | /// that map to a set of 2 regular diagnostic ids each and which are used to |
1530 | /// simplify emitting compatibility warnings. |
1531 | void clang::EmitClangDiagsCompatIDs(const llvm::RecordKeeper &Records, |
1532 | llvm::raw_ostream &OS, |
1533 | const std::string &Component) { |
1534 | ArrayRef<const Record *> Ids = |
1535 | Records.getAllDerivedDefinitions(ClassName: "CompatWarningId"); |
1536 | |
1537 | StringRef PrevComponent = ""; |
1538 | for (auto [I, R] : enumerate(First: make_pointee_range(Range&: Ids))) { |
1539 | StringRef DiagComponent = R.getValueAsString(FieldName: "Component"); |
1540 | if (!Component.empty() && Component != DiagComponent) |
1541 | continue; |
1542 | |
1543 | StringRef CompatDiagName = R.getValueAsString(FieldName: "Name"); |
1544 | StringRef Diag = R.getValueAsString(FieldName: "Diag"); |
1545 | StringRef DiagPre = R.getValueAsString(FieldName: "DiagPre"); |
1546 | int64_t CXXStdVer = R.getValueAsInt(FieldName: "Std"); |
1547 | |
1548 | // We don't want to create empty enums since some compilers (including |
1549 | // Clang) warn about that, so these macros are used to avoid having to |
1550 | // unconditionally write 'enum {' and '};' in the headers. |
1551 | if (PrevComponent != DiagComponent) { |
1552 | if (!PrevComponent.empty()) |
1553 | OS << "DIAG_COMPAT_IDS_END()\n"; |
1554 | OS << "DIAG_COMPAT_IDS_BEGIN()\n"; |
1555 | PrevComponent = DiagComponent; |
1556 | } |
1557 | |
1558 | // FIXME: We sometimes define multiple compat diagnostics with the same |
1559 | // name, e.g. 'constexpr_body_invalid_stmt' exists for C++14/20/23. It would |
1560 | // be nice if we could combine all of them into a single compatibility diag |
1561 | // id. |
1562 | OS << "DIAG_COMPAT_ID("<< I << ","; |
1563 | OS << CompatDiagName << ","<< CXXStdVer << ","<< Diag << ","<< DiagPre; |
1564 | OS << ")\n"; |
1565 | } |
1566 | |
1567 | if (!PrevComponent.empty()) |
1568 | OS << "DIAG_COMPAT_IDS_END()\n"; |
1569 | } |
1570 | |
1571 | /// ClangDiagsIntefaceEmitter - Emit the diagnostics interface header for |
1572 | /// a Clang component. |
1573 | void clang::EmitClangDiagsInterface(llvm::raw_ostream &OS, |
1574 | const std::string &Component) { |
1575 | if (Component.empty()) |
1576 | PrintFatalError(Msg: "'-gen-clang-diags-iface' requires a component name"); |
1577 | |
1578 | std::string ComponentUpper = StringRef(Component).upper(); |
1579 | const char *Comp = Component.c_str(); |
1580 | const char *Upper = ComponentUpper.c_str(); |
1581 | |
1582 | OS << llvm::format(Fmt: R"c++( |
1583 | namespace clang { |
1584 | namespace diag { |
1585 | enum { |
1586 | #define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ |
1587 | SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ |
1588 | ENUM, |
1589 | #define %sSTART |
1590 | #include "clang/Basic/Diagnostic%sKinds.inc" |
1591 | #undef DIAG |
1592 | NUM_BUILTIN_%s_DIAGNOSTICS |
1593 | }; |
1594 | |
1595 | #define DIAG_ENUM(ENUM_NAME) \ |
1596 | namespace ENUM_NAME { \ |
1597 | enum { |
1598 | #define DIAG_ENUM_ITEM(IDX, NAME) NAME = IDX, |
1599 | #define DIAG_ENUM_END() \ |
1600 | } \ |
1601 | ; \ |
1602 | } |
1603 | #include "clang/Basic/Diagnostic%sEnums.inc" |
1604 | #undef DIAG_ENUM_END |
1605 | #undef DIAG_ENUM_ITEM |
1606 | #undef DIAG_ENUM |
1607 | } // end namespace diag |
1608 | |
1609 | namespace diag_compat { |
1610 | #define DIAG_COMPAT_IDS_BEGIN() enum { |
1611 | #define DIAG_COMPAT_IDS_END() \ |
1612 | } \ |
1613 | ; |
1614 | #define DIAG_COMPAT_ID(IDX, NAME, ...) NAME = IDX, |
1615 | #include "clang/Basic/Diagnostic%sCompatIDs.inc" |
1616 | #undef DIAG_COMPAT_ID |
1617 | #undef DIAG_COMPAT_IDS_BEGIN |
1618 | #undef DIAG_COMPAT_IDS_END |
1619 | } // end namespace diag_compat |
1620 | } // end namespace clang |
1621 | )c++", |
1622 | Vals: Upper, Vals: Comp, Vals: Upper, Vals: Comp, Vals: Comp); |
1623 | } |
1624 | |
1625 | /// ClangDiagsEnumsEmitter - The top-level class emits .def files containing |
1626 | /// declarations of Clang diagnostic enums for selects. |
1627 | void clang::EmitClangDiagsEnums(const RecordKeeper &Records, raw_ostream &OS, |
1628 | const std::string &Component) { |
1629 | DiagnosticTextBuilder DiagTextBuilder(Records); |
1630 | ArrayRef<const Record *> Diags = |
1631 | Records.getAllDerivedDefinitions(ClassName: "Diagnostic"); |
1632 | |
1633 | llvm::SmallVector<std::pair<const Record *, std::string>> EnumerationNames; |
1634 | |
1635 | for (const Record &R : make_pointee_range(Range&: Diags)) { |
1636 | DiagEnumPrinter::ResultTy Enums = DiagTextBuilder.buildForEnum(R: &R); |
1637 | |
1638 | for (auto &Enumeration : Enums) { |
1639 | bool ShouldPrint = |
1640 | Component.empty() || Component == R.getValueAsString(FieldName: "Component"); |
1641 | |
1642 | auto PreviousByName = llvm::find_if(Range&: EnumerationNames, P: [&](auto &Prev) { |
1643 | return Prev.second == Enumeration.first; |
1644 | }); |
1645 | |
1646 | if (PreviousByName != EnumerationNames.end()) { |
1647 | PrintError(Rec: &R, |
1648 | Msg: "Duplicate enumeration name '"+ Enumeration.first + "'"); |
1649 | PrintNote(NoteLoc: PreviousByName->first->getLoc(), |
1650 | Msg: "Previous diagnostic is here"); |
1651 | } |
1652 | |
1653 | EnumerationNames.emplace_back(Args: &R, Args&: Enumeration.first); |
1654 | |
1655 | if (ShouldPrint) |
1656 | OS << "DIAG_ENUM("<< Enumeration.first << ")\n"; |
1657 | |
1658 | llvm::SmallVector<std::string> EnumeratorNames; |
1659 | for (auto &Enumerator : Enumeration.second) { |
1660 | if (llvm::find(Range&: EnumeratorNames, Val: Enumerator.second) != |
1661 | EnumeratorNames.end()) |
1662 | PrintError(Rec: &R, |
1663 | Msg: "Duplicate enumerator name '"+ Enumerator.second + "'"); |
1664 | EnumeratorNames.push_back(Elt: Enumerator.second); |
1665 | |
1666 | if (ShouldPrint) |
1667 | OS << "DIAG_ENUM_ITEM("<< Enumerator.first << ", " |
1668 | << Enumerator.second << ")\n"; |
1669 | } |
1670 | if (ShouldPrint) |
1671 | OS << "DIAG_ENUM_END()\n"; |
1672 | } |
1673 | } |
1674 | } |
1675 | |
1676 | /// ClangDiagsDefsEmitter - The top-level class emits .def files containing |
1677 | /// declarations of Clang diagnostics. |
1678 | void clang::EmitClangDiagsDefs(const RecordKeeper &Records, raw_ostream &OS, |
1679 | const std::string &Component) { |
1680 | // Write the #if guard |
1681 | if (!Component.empty()) { |
1682 | std::string ComponentName = StringRef(Component).upper(); |
1683 | OS << "#ifdef "<< ComponentName << "START\n"; |
1684 | OS << "__"<< ComponentName << "START = DIAG_START_"<< ComponentName |
1685 | << ",\n"; |
1686 | OS << "#undef "<< ComponentName << "START\n"; |
1687 | OS << "#endif\n\n"; |
1688 | } |
1689 | |
1690 | DiagnosticTextBuilder DiagTextBuilder(Records); |
1691 | |
1692 | ArrayRef<const Record *> Diags = |
1693 | Records.getAllDerivedDefinitions(ClassName: "Diagnostic"); |
1694 | |
1695 | ArrayRef<const Record *> DiagGroups = |
1696 | Records.getAllDerivedDefinitions(ClassName: "DiagGroup"); |
1697 | |
1698 | DiagsInGroupTy DiagsInGroup; |
1699 | groupDiagnostics(Diags, DiagGroups, DiagsInGroup); |
1700 | |
1701 | DiagCategoryIDMap CategoryIDs(Records); |
1702 | DiagGroupParentMap DGParentMap(Records); |
1703 | |
1704 | // Compute the set of diagnostics that are in -Wpedantic. |
1705 | RecordSet DiagsInPedantic; |
1706 | InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup); |
1707 | inferPedantic.compute(DiagsInPedantic: &DiagsInPedantic, GroupsInPedantic: (RecordVec*)nullptr); |
1708 | |
1709 | for (const Record &R : make_pointee_range(Range&: Diags)) { |
1710 | // Check if this is an error that is accidentally in a warning |
1711 | // group. |
1712 | if (isError(Diag: R)) { |
1713 | if (const auto *Group = dyn_cast<DefInit>(Val: R.getValueInit(FieldName: "Group"))) { |
1714 | const Record *GroupRec = Group->getDef(); |
1715 | StringRef GroupName = GroupRec->getValueAsString(FieldName: "GroupName"); |
1716 | PrintFatalError(ErrorLoc: R.getLoc(), Msg: "Error "+ R.getName() + |
1717 | " cannot be in a warning group ["+ GroupName + "]"); |
1718 | } |
1719 | } |
1720 | |
1721 | // Check that all remarks have an associated diagnostic group. |
1722 | if (isRemark(Diag: R)) { |
1723 | if (!isa<DefInit>(Val: R.getValueInit(FieldName: "Group"))) { |
1724 | PrintFatalError(ErrorLoc: R.getLoc(), Msg: "Error "+ R.getName() + |
1725 | " not in any diagnostic group"); |
1726 | } |
1727 | } |
1728 | |
1729 | // Filter by component. |
1730 | if (!Component.empty() && Component != R.getValueAsString(FieldName: "Component")) |
1731 | continue; |
1732 | |
1733 | // Validate diagnostic wording for common issues. |
1734 | verifyDiagnosticWording(Diag: R); |
1735 | |
1736 | OS << "DIAG("<< R.getName() << ", "; |
1737 | OS << R.getValueAsDef(FieldName: "Class")->getName(); |
1738 | OS << ", (unsigned)diag::Severity::" |
1739 | << R.getValueAsDef(FieldName: "DefaultSeverity")->getValueAsString(FieldName: "Name"); |
1740 | |
1741 | // Description string. |
1742 | OS << ", \""; |
1743 | OS.write_escaped(Str: DiagTextBuilder.buildForDefinition(R: &R)) << '"'; |
1744 | |
1745 | // Warning group associated with the diagnostic. This is stored as an index |
1746 | // into the alphabetically sorted warning group table. |
1747 | if (const auto *DI = dyn_cast<DefInit>(Val: R.getValueInit(FieldName: "Group"))) { |
1748 | auto I = DiagsInGroup.find(x: DI->getDef()->getValueAsString(FieldName: "GroupName")); |
1749 | assert(I != DiagsInGroup.end()); |
1750 | OS << ", "<< I->second.IDNo; |
1751 | } else if (DiagsInPedantic.count(V: &R)) { |
1752 | auto I = DiagsInGroup.find(x: "pedantic"); |
1753 | assert(I != DiagsInGroup.end() && "pedantic group not defined"); |
1754 | OS << ", "<< I->second.IDNo; |
1755 | } else { |
1756 | OS << ", 0"; |
1757 | } |
1758 | |
1759 | // SFINAE response. |
1760 | OS << ", "<< R.getValueAsDef(FieldName: "SFINAE")->getName(); |
1761 | |
1762 | // Default warning has no Werror bit. |
1763 | if (R.getValueAsBit(FieldName: "WarningNoWerror")) |
1764 | OS << ", true"; |
1765 | else |
1766 | OS << ", false"; |
1767 | |
1768 | if (R.getValueAsBit(FieldName: "ShowInSystemHeader")) |
1769 | OS << ", true"; |
1770 | else |
1771 | OS << ", false"; |
1772 | |
1773 | if (R.getValueAsBit(FieldName: "ShowInSystemMacro")) |
1774 | OS << ", true"; |
1775 | else |
1776 | OS << ", false"; |
1777 | |
1778 | if (R.getValueAsBit(FieldName: "Deferrable")) |
1779 | OS << ", true"; |
1780 | else |
1781 | OS << ", false"; |
1782 | |
1783 | // Category number. |
1784 | OS << ", "<< CategoryIDs.getID(CategoryString: getDiagnosticCategory(R: &R, DiagGroupParents&: DGParentMap)); |
1785 | OS << ")\n"; |
1786 | } |
1787 | } |
1788 | |
1789 | //===----------------------------------------------------------------------===// |
1790 | // Warning Group Tables generation |
1791 | //===----------------------------------------------------------------------===// |
1792 | |
1793 | static std::string getDiagCategoryEnum(StringRef name) { |
1794 | if (name.empty()) |
1795 | return "DiagCat_None"; |
1796 | SmallString<256> enumName = StringRef("DiagCat_"); |
1797 | for (StringRef::iterator I = name.begin(), E = name.end(); I != E; ++I) |
1798 | enumName += isalnum(*I) ? *I : '_'; |
1799 | return std::string(enumName); |
1800 | } |
1801 | |
1802 | /// Emit the array of diagnostic subgroups. |
1803 | /// |
1804 | /// The array of diagnostic subgroups contains for each group a list of its |
1805 | /// subgroups. The individual lists are separated by '-1'. Groups with no |
1806 | /// subgroups are skipped. |
1807 | /// |
1808 | /// \code |
1809 | /// static const int16_t DiagSubGroups[] = { |
1810 | /// /* Empty */ -1, |
1811 | /// /* DiagSubGroup0 */ 142, -1, |
1812 | /// /* DiagSubGroup13 */ 265, 322, 399, -1 |
1813 | /// } |
1814 | /// \endcode |
1815 | /// |
1816 | static void emitDiagSubGroups(DiagsInGroupTy &DiagsInGroup, |
1817 | RecordVec &GroupsInPedantic, raw_ostream &OS) { |
1818 | OS << "static const int16_t DiagSubGroups[] = {\n" |
1819 | << " /* Empty */ -1,\n"; |
1820 | for (auto const &[Name, Group] : DiagsInGroup) { |
1821 | const bool IsPedantic = Name == "pedantic"; |
1822 | const std::vector<StringRef> &SubGroups = Group.SubGroups; |
1823 | if (!SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty())) { |
1824 | OS << " /* DiagSubGroup"<< Group.IDNo << " */ "; |
1825 | for (StringRef SubGroup : SubGroups) { |
1826 | auto RI = DiagsInGroup.find(x: SubGroup); |
1827 | assert(RI != DiagsInGroup.end() && "Referenced without existing?"); |
1828 | OS << RI->second.IDNo << ", "; |
1829 | } |
1830 | // Emit the groups implicitly in "pedantic". |
1831 | if (IsPedantic) { |
1832 | for (auto const &Group : GroupsInPedantic) { |
1833 | StringRef GroupName = Group->getValueAsString(FieldName: "GroupName"); |
1834 | auto RI = DiagsInGroup.find(x: GroupName); |
1835 | assert(RI != DiagsInGroup.end() && "Referenced without existing?"); |
1836 | OS << RI->second.IDNo << ", "; |
1837 | } |
1838 | } |
1839 | |
1840 | OS << "-1,\n"; |
1841 | } |
1842 | } |
1843 | OS << "};\n\n"; |
1844 | } |
1845 | |
1846 | /// Emit the list of diagnostic arrays. |
1847 | /// |
1848 | /// This data structure is a large array that contains itself arrays of varying |
1849 | /// size. Each array represents a list of diagnostics. The different arrays are |
1850 | /// separated by the value '-1'. |
1851 | /// |
1852 | /// \code |
1853 | /// static const int16_t DiagArrays[] = { |
1854 | /// /* Empty */ -1, |
1855 | /// /* DiagArray1 */ diag::warn_pragma_message, |
1856 | /// -1, |
1857 | /// /* DiagArray2 */ diag::warn_abs_too_small, |
1858 | /// diag::warn_unsigned_abs, |
1859 | /// diag::warn_wrong_absolute_value_type, |
1860 | /// -1 |
1861 | /// }; |
1862 | /// \endcode |
1863 | /// |
1864 | static void emitDiagArrays(DiagsInGroupTy &DiagsInGroup, |
1865 | RecordVec &DiagsInPedantic, raw_ostream &OS) { |
1866 | OS << "static const int16_t DiagArrays[] = {\n" |
1867 | << " /* Empty */ -1,\n"; |
1868 | for (const auto &[Name, Group] : DiagsInGroup) { |
1869 | const bool IsPedantic = Name == "pedantic"; |
1870 | |
1871 | const std::vector<const Record *> &V = Group.DiagsInGroup; |
1872 | if (!V.empty() || (IsPedantic && !DiagsInPedantic.empty())) { |
1873 | OS << " /* DiagArray"<< Group.IDNo << " */ "; |
1874 | for (auto *Record : V) |
1875 | OS << "diag::"<< Record->getName() << ", "; |
1876 | // Emit the diagnostics implicitly in "pedantic". |
1877 | if (IsPedantic) { |
1878 | for (auto const &Diag : DiagsInPedantic) |
1879 | OS << "diag::"<< Diag->getName() << ", "; |
1880 | } |
1881 | OS << "-1,\n"; |
1882 | } |
1883 | } |
1884 | OS << "};\n\n"; |
1885 | } |
1886 | |
1887 | /// Emit a list of group names. |
1888 | /// |
1889 | /// This creates an `llvm::StringTable` of all the diagnostic group names. |
1890 | static void emitDiagGroupNames(const StringToOffsetTable &GroupNames, |
1891 | raw_ostream &OS) { |
1892 | GroupNames.EmitStringTableDef(OS, Name: "DiagGroupNames"); |
1893 | OS << "\n"; |
1894 | } |
1895 | |
1896 | /// Emit diagnostic arrays and related data structures. |
1897 | /// |
1898 | /// This creates the actual diagnostic array, an array of diagnostic subgroups |
1899 | /// and an array of subgroup names. |
1900 | /// |
1901 | /// \code |
1902 | /// #ifdef GET_DIAG_ARRAYS |
1903 | /// static const int16_t DiagArrays[]; |
1904 | /// static const int16_t DiagSubGroups[]; |
1905 | /// static constexpr llvm::StringTable DiagGroupNames; |
1906 | /// #endif |
1907 | /// \endcode |
1908 | static void emitAllDiagArrays(DiagsInGroupTy &DiagsInGroup, |
1909 | RecordVec &DiagsInPedantic, |
1910 | RecordVec &GroupsInPedantic, |
1911 | const StringToOffsetTable &GroupNames, |
1912 | raw_ostream &OS) { |
1913 | OS << "\n#ifdef GET_DIAG_ARRAYS\n"; |
1914 | emitDiagArrays(DiagsInGroup, DiagsInPedantic, OS); |
1915 | emitDiagSubGroups(DiagsInGroup, GroupsInPedantic, OS); |
1916 | emitDiagGroupNames(GroupNames, OS); |
1917 | OS << "#endif // GET_DIAG_ARRAYS\n\n"; |
1918 | } |
1919 | |
1920 | /// Emit diagnostic table. |
1921 | /// |
1922 | /// The table is sorted by the name of the diagnostic group. Each element |
1923 | /// consists of the name of the diagnostic group (given as offset in the |
1924 | /// group name table), a reference to a list of diagnostics (optional) and a |
1925 | /// reference to a set of subgroups (optional). |
1926 | /// |
1927 | /// \code |
1928 | /// #ifdef GET_DIAG_TABLE |
1929 | /// {/* abi */ 159, /* DiagArray11 */ 19, /* Empty */ 0}, |
1930 | /// {/* aggregate-return */ 180, /* Empty */ 0, /* Empty */ 0}, |
1931 | /// {/* all */ 197, /* Empty */ 0, /* DiagSubGroup13 */ 3}, |
1932 | /// {/* deprecated */ 1981,/* DiagArray1 */ 348, /* DiagSubGroup3 */ 9}, |
1933 | /// #endif |
1934 | /// \endcode |
1935 | static void emitDiagTable(DiagsInGroupTy &DiagsInGroup, |
1936 | RecordVec &DiagsInPedantic, |
1937 | RecordVec &GroupsInPedantic, |
1938 | const StringToOffsetTable &GroupNames, |
1939 | raw_ostream &OS) { |
1940 | unsigned MaxLen = 0; |
1941 | |
1942 | for (auto const &I: DiagsInGroup) |
1943 | MaxLen = std::max(a: MaxLen, b: (unsigned)I.first.size()); |
1944 | |
1945 | OS << "\n#ifdef DIAG_ENTRY\n"; |
1946 | unsigned SubGroupIndex = 1, DiagArrayIndex = 1; |
1947 | for (auto const &[Name, GroupInfo] : DiagsInGroup) { |
1948 | // Group option string. |
1949 | OS << "DIAG_ENTRY("; |
1950 | OS << GroupInfo.GroupName << " /* "; |
1951 | |
1952 | if (Name.find_first_not_of(Chars: "abcdefghijklmnopqrstuvwxyz" |
1953 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
1954 | "0123456789!@#$%^*-+=:?") != std::string::npos) |
1955 | PrintFatalError(Msg: "Invalid character in diagnostic group '"+ Name + "'"); |
1956 | OS << Name << " */, "; |
1957 | OS << *GroupNames.GetStringOffset(Str: Name) << ", "; |
1958 | |
1959 | // Special handling for 'pedantic'. |
1960 | const bool IsPedantic = Name == "pedantic"; |
1961 | |
1962 | // Diagnostics in the group. |
1963 | const std::vector<const Record *> &V = GroupInfo.DiagsInGroup; |
1964 | const bool hasDiags = |
1965 | !V.empty() || (IsPedantic && !DiagsInPedantic.empty()); |
1966 | if (hasDiags) { |
1967 | OS << "/* DiagArray"<< GroupInfo.IDNo << " */ "<< DiagArrayIndex |
1968 | << ", "; |
1969 | if (IsPedantic) |
1970 | DiagArrayIndex += DiagsInPedantic.size(); |
1971 | DiagArrayIndex += V.size() + 1; |
1972 | } else { |
1973 | OS << "0, "; |
1974 | } |
1975 | |
1976 | // Subgroups. |
1977 | const std::vector<StringRef> &SubGroups = GroupInfo.SubGroups; |
1978 | const bool hasSubGroups = |
1979 | !SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty()); |
1980 | if (hasSubGroups) { |
1981 | OS << "/* DiagSubGroup"<< GroupInfo.IDNo << " */ "<< SubGroupIndex |
1982 | << ", "; |
1983 | if (IsPedantic) |
1984 | SubGroupIndex += GroupsInPedantic.size(); |
1985 | SubGroupIndex += SubGroups.size() + 1; |
1986 | } else { |
1987 | OS << "0, "; |
1988 | } |
1989 | |
1990 | std::string Documentation = GroupInfo.Defs.back() |
1991 | ->getValue(Name: "Documentation") |
1992 | ->getValue() |
1993 | ->getAsUnquotedString(); |
1994 | |
1995 | OS << "R\"("<< StringRef(Documentation).trim() << ")\""; |
1996 | |
1997 | OS << ")\n"; |
1998 | } |
1999 | OS << "#endif // DIAG_ENTRY\n\n"; |
2000 | } |
2001 | |
2002 | /// Emit the table of diagnostic categories. |
2003 | /// |
2004 | /// The table has the form of macro calls that have two parameters. The |
2005 | /// category's name as well as an enum that represents the category. The |
2006 | /// table can be used by defining the macro 'CATEGORY' and including this |
2007 | /// table right after. |
2008 | /// |
2009 | /// \code |
2010 | /// #ifdef GET_CATEGORY_TABLE |
2011 | /// CATEGORY("Semantic Issue", DiagCat_Semantic_Issue) |
2012 | /// CATEGORY("Lambda Issue", DiagCat_Lambda_Issue) |
2013 | /// #endif |
2014 | /// \endcode |
2015 | static void emitCategoryTable(const RecordKeeper &Records, raw_ostream &OS) { |
2016 | DiagCategoryIDMap CategoriesByID(Records); |
2017 | OS << "\n#ifdef GET_CATEGORY_TABLE\n"; |
2018 | for (auto const &C : CategoriesByID) |
2019 | OS << "CATEGORY(\""<< C << "\", "<< getDiagCategoryEnum(name: C) << ")\n"; |
2020 | OS << "#endif // GET_CATEGORY_TABLE\n\n"; |
2021 | } |
2022 | |
2023 | void clang::EmitClangDiagGroups(const RecordKeeper &Records, raw_ostream &OS) { |
2024 | // Compute a mapping from a DiagGroup to all of its parents. |
2025 | DiagGroupParentMap DGParentMap(Records); |
2026 | |
2027 | ArrayRef<const Record *> Diags = |
2028 | Records.getAllDerivedDefinitions(ClassName: "Diagnostic"); |
2029 | |
2030 | ArrayRef<const Record *> DiagGroups = |
2031 | Records.getAllDerivedDefinitions(ClassName: "DiagGroup"); |
2032 | |
2033 | DiagsInGroupTy DiagsInGroup; |
2034 | groupDiagnostics(Diags, DiagGroups, DiagsInGroup); |
2035 | |
2036 | // All extensions are implicitly in the "pedantic" group. Record the |
2037 | // implicit set of groups in the "pedantic" group, and use this information |
2038 | // later when emitting the group information for Pedantic. |
2039 | RecordVec DiagsInPedantic; |
2040 | RecordVec GroupsInPedantic; |
2041 | InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup); |
2042 | inferPedantic.compute(DiagsInPedantic: &DiagsInPedantic, GroupsInPedantic: &GroupsInPedantic); |
2043 | |
2044 | StringToOffsetTable GroupNames; |
2045 | for (const auto &[Name, Group] : DiagsInGroup) { |
2046 | GroupNames.GetOrAddStringOffset(Str: Name); |
2047 | } |
2048 | |
2049 | emitAllDiagArrays(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames, |
2050 | OS); |
2051 | emitDiagTable(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames, |
2052 | OS); |
2053 | emitCategoryTable(Records, OS); |
2054 | } |
2055 | |
2056 | //===----------------------------------------------------------------------===// |
2057 | // Diagnostic name index generation |
2058 | //===----------------------------------------------------------------------===// |
2059 | |
2060 | void clang::EmitClangDiagsIndexName(const RecordKeeper &Records, |
2061 | raw_ostream &OS) { |
2062 | std::vector<const Record *> Diags = |
2063 | Records.getAllDerivedDefinitions(ClassName: "Diagnostic"); |
2064 | |
2065 | sort(C&: Diags, Comp: [](const Record *LHS, const Record *RHS) { |
2066 | return LHS->getName() < RHS->getName(); |
2067 | }); |
2068 | |
2069 | for (const Record *Elem : Diags) |
2070 | OS << "DIAG_NAME_INDEX("<< Elem->getName() << ")\n"; |
2071 | } |
2072 | |
2073 | //===----------------------------------------------------------------------===// |
2074 | // Diagnostic documentation generation |
2075 | //===----------------------------------------------------------------------===// |
2076 | |
2077 | namespace docs { |
2078 | namespace { |
2079 | |
2080 | bool isRemarkGroup(const Record *DiagGroup, |
2081 | const DiagsInGroupTy &DiagsInGroup) { |
2082 | bool AnyRemarks = false, AnyNonRemarks = false; |
2083 | |
2084 | std::function<void(StringRef)> Visit = [&](StringRef GroupName) { |
2085 | auto &GroupInfo = DiagsInGroup.find(x: GroupName)->second; |
2086 | for (const Record *Diag : GroupInfo.DiagsInGroup) |
2087 | (isRemark(Diag: *Diag) ? AnyRemarks : AnyNonRemarks) = true; |
2088 | for (StringRef Name : GroupInfo.SubGroups) |
2089 | Visit(Name); |
2090 | }; |
2091 | Visit(DiagGroup->getValueAsString(FieldName: "GroupName")); |
2092 | |
2093 | if (AnyRemarks && AnyNonRemarks) |
2094 | PrintFatalError( |
2095 | ErrorLoc: DiagGroup->getLoc(), |
2096 | Msg: "Diagnostic group contains both remark and non-remark diagnostics"); |
2097 | return AnyRemarks; |
2098 | } |
2099 | |
2100 | std::string getDefaultSeverity(const Record *Diag) { |
2101 | return std::string( |
2102 | Diag->getValueAsDef(FieldName: "DefaultSeverity")->getValueAsString(FieldName: "Name")); |
2103 | } |
2104 | |
2105 | std::set<std::string> getDefaultSeverities(const Record *DiagGroup, |
2106 | const DiagsInGroupTy &DiagsInGroup) { |
2107 | std::set<std::string> States; |
2108 | |
2109 | std::function<void(StringRef)> Visit = [&](StringRef GroupName) { |
2110 | auto &GroupInfo = DiagsInGroup.find(x: GroupName)->second; |
2111 | for (const Record *Diag : GroupInfo.DiagsInGroup) |
2112 | States.insert(x: getDefaultSeverity(Diag)); |
2113 | for (const auto &Name : GroupInfo.SubGroups) |
2114 | Visit(Name); |
2115 | }; |
2116 | Visit(DiagGroup->getValueAsString(FieldName: "GroupName")); |
2117 | return States; |
2118 | } |
2119 | |
2120 | void writeHeader(StringRef Str, raw_ostream &OS, char Kind = '-') { |
2121 | OS << Str << "\n"<< std::string(Str.size(), Kind) << "\n"; |
2122 | } |
2123 | |
2124 | void writeDiagnosticText(DiagnosticTextBuilder &Builder, const Record *R, |
2125 | StringRef Role, raw_ostream &OS) { |
2126 | StringRef Text = R->getValueAsString(FieldName: "Summary"); |
2127 | if (Text == "%0") |
2128 | OS << "The text of this diagnostic is not controlled by Clang.\n\n"; |
2129 | else { |
2130 | std::vector<std::string> Out = Builder.buildForDocumentation(Severity: Role, R); |
2131 | for (auto &Line : Out) |
2132 | OS << Line << "\n"; |
2133 | OS << "\n"; |
2134 | } |
2135 | } |
2136 | |
2137 | } // namespace |
2138 | } // namespace docs |
2139 | |
2140 | void clang::EmitClangDiagDocs(const RecordKeeper &Records, raw_ostream &OS) { |
2141 | using namespace docs; |
2142 | |
2143 | // Get the documentation introduction paragraph. |
2144 | const Record *Documentation = Records.getDef(Name: "GlobalDocumentation"); |
2145 | if (!Documentation) { |
2146 | PrintFatalError(Msg: "The Documentation top-level definition is missing, " |
2147 | "no documentation will be generated."); |
2148 | return; |
2149 | } |
2150 | |
2151 | OS << Documentation->getValueAsString(FieldName: "Intro") << "\n"; |
2152 | |
2153 | DiagnosticTextBuilder Builder(Records); |
2154 | |
2155 | ArrayRef<const Record *> Diags = |
2156 | Records.getAllDerivedDefinitions(ClassName: "Diagnostic"); |
2157 | |
2158 | std::vector<const Record *> DiagGroups = |
2159 | Records.getAllDerivedDefinitions(ClassName: "DiagGroup"); |
2160 | sort(C&: DiagGroups, Comp: diagGroupBeforeByName); |
2161 | |
2162 | DiagGroupParentMap DGParentMap(Records); |
2163 | |
2164 | DiagsInGroupTy DiagsInGroup; |
2165 | groupDiagnostics(Diags, DiagGroups, DiagsInGroup); |
2166 | |
2167 | // Compute the set of diagnostics that are in -Wpedantic. |
2168 | { |
2169 | RecordSet DiagsInPedanticSet; |
2170 | RecordSet GroupsInPedanticSet; |
2171 | InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup); |
2172 | inferPedantic.compute(DiagsInPedantic: &DiagsInPedanticSet, GroupsInPedantic: &GroupsInPedanticSet); |
2173 | auto &PedDiags = DiagsInGroup["pedantic"]; |
2174 | // Put the diagnostics into a deterministic order. |
2175 | RecordVec DiagsInPedantic(DiagsInPedanticSet.begin(), |
2176 | DiagsInPedanticSet.end()); |
2177 | RecordVec GroupsInPedantic(GroupsInPedanticSet.begin(), |
2178 | GroupsInPedanticSet.end()); |
2179 | sort(C&: DiagsInPedantic, Comp: beforeThanCompare); |
2180 | sort(C&: GroupsInPedantic, Comp: beforeThanCompare); |
2181 | PedDiags.DiagsInGroup.insert(position: PedDiags.DiagsInGroup.end(), |
2182 | first: DiagsInPedantic.begin(), |
2183 | last: DiagsInPedantic.end()); |
2184 | for (auto *Group : GroupsInPedantic) |
2185 | PedDiags.SubGroups.push_back(x: Group->getValueAsString(FieldName: "GroupName")); |
2186 | } |
2187 | |
2188 | // FIXME: Write diagnostic categories and link to diagnostic groups in each. |
2189 | |
2190 | // Write out the diagnostic groups. |
2191 | for (const Record *G : DiagGroups) { |
2192 | bool IsRemarkGroup = isRemarkGroup(DiagGroup: G, DiagsInGroup); |
2193 | auto &GroupInfo = DiagsInGroup[G->getValueAsString(FieldName: "GroupName")]; |
2194 | bool IsSynonym = GroupInfo.DiagsInGroup.empty() && |
2195 | GroupInfo.SubGroups.size() == 1; |
2196 | |
2197 | writeHeader(Str: ((IsRemarkGroup ? "-R": "-W") + |
2198 | G->getValueAsString(FieldName: "GroupName")).str(), |
2199 | OS); |
2200 | |
2201 | if (!IsSynonym) { |
2202 | // FIXME: Ideally, all the diagnostics in a group should have the same |
2203 | // default state, but that is not currently the case. |
2204 | auto DefaultSeverities = getDefaultSeverities(DiagGroup: G, DiagsInGroup); |
2205 | if (!DefaultSeverities.empty() && !DefaultSeverities.count(x: "Ignored")) { |
2206 | bool AnyNonErrors = DefaultSeverities.count(x: "Warning") || |
2207 | DefaultSeverities.count(x: "Remark"); |
2208 | if (!AnyNonErrors) |
2209 | OS << "This diagnostic is an error by default, but the flag ``-Wno-" |
2210 | << G->getValueAsString(FieldName: "GroupName") << "`` can be used to disable " |
2211 | << "the error.\n\n"; |
2212 | else |
2213 | OS << "This diagnostic is enabled by default.\n\n"; |
2214 | } else if (DefaultSeverities.size() > 1) { |
2215 | OS << "Some of the diagnostics controlled by this flag are enabled " |
2216 | << "by default.\n\n"; |
2217 | } |
2218 | } |
2219 | |
2220 | if (!GroupInfo.SubGroups.empty()) { |
2221 | if (IsSynonym) |
2222 | OS << "Synonym for "; |
2223 | else if (GroupInfo.DiagsInGroup.empty()) |
2224 | OS << "Controls "; |
2225 | else |
2226 | OS << "Also controls "; |
2227 | |
2228 | bool First = true; |
2229 | sort(C&: GroupInfo.SubGroups); |
2230 | for (StringRef Name : GroupInfo.SubGroups) { |
2231 | if (!First) OS << ", "; |
2232 | OS << "`"<< (IsRemarkGroup ? "-R": "-W") << Name << "`_"; |
2233 | First = false; |
2234 | } |
2235 | OS << ".\n\n"; |
2236 | } |
2237 | |
2238 | if (!GroupInfo.DiagsInGroup.empty()) { |
2239 | OS << "**Diagnostic text:**\n\n"; |
2240 | for (const Record *D : GroupInfo.DiagsInGroup) { |
2241 | auto Severity = getDefaultSeverity(Diag: D); |
2242 | Severity[0] = tolower(c: Severity[0]); |
2243 | if (Severity == "ignored") |
2244 | Severity = IsRemarkGroup ? "remark": "warning"; |
2245 | |
2246 | writeDiagnosticText(Builder, R: D, Role: Severity, OS); |
2247 | } |
2248 | } |
2249 | |
2250 | auto Doc = G->getValueAsString(FieldName: "Documentation"); |
2251 | if (!Doc.empty()) |
2252 | OS << Doc; |
2253 | else if (GroupInfo.SubGroups.empty() && GroupInfo.DiagsInGroup.empty()) |
2254 | OS << "This diagnostic flag exists for GCC compatibility, and has no " |
2255 | "effect in Clang.\n"; |
2256 | OS << "\n"; |
2257 | } |
2258 | } |
2259 |
Definitions
- DiagGroupParentMap
- DiagGroupParentMap
- getParents
- getCategoryFromDiagGroup
- getDiagnosticCategory
- DiagCategoryIDMap
- DiagCategoryIDMap
- getID
- begin
- end
- GroupInfo
- GroupInfo
- beforeThanCompare
- diagGroupBeforeByName
- groupDiagnostics
- InferPedantic
- InferPedantic
- isSubGroupOfGroup
- isExtension
- isOffByDefault
- groupInPedantic
- markGroup
- compute
- PieceKind
- ModifierType
- getModifierName
- Piece
- Piece
- Piece
- operator=
- ~Piece
- getPieceClass
- classof
- MultiPiece
- MultiPiece
- MultiPiece
- classof
- TextPiece
- TextPiece
- classof
- PlaceholderPiece
- PlaceholderPiece
- classof
- SelectPiece
- SelectPiece
- SelectPiece
- classof
- EnumSelectPiece
- EnumSelectPiece
- classof
- EnumValuePiece
- PluralPiece
- PluralPiece
- classof
- DiffPiece
- DiffPiece
- classof
- SubstitutionPiece
- SubstitutionPiece
- classof
- DiagnosticTextBuilder
- DiagnosticTextBuilder
- operator=
- DiagnosticTextBuilder
- getSubstitution
- PrintFatalError
- DiagText
- New
- DiagText
- StopAt
- DiagText
- operator=
- DiagText
- operator=
- ~DiagText
- EvaluatingRecordGuard
- EvaluatingRecordGuard
- ~EvaluatingRecordGuard
- DiagTextVisitor
- getDerived
- getSubstitutionMappings
- SubstitutionContext
- SubstitutionContext
- ~SubstitutionContext
- DiagTextVisitor
- Visit
- VisitSubstitution
- mapIndex
- mapIndex
- escapeRST
- padToSameLength
- makeTableRows
- makeRowSeparator
- DiagTextDocPrinter
- DiagTextDocPrinter
- gatherNodes
- VisitMulti
- VisitText
- VisitPlaceholder
- VisitSelect
- VisitEnumSelect
- VisitPlural
- VisitDiff
- DiagEnumPrinter
- DiagEnumPrinter
- VisitMulti
- VisitText
- VisitPlaceholder
- VisitDiff
- VisitSelect
- VisitPlural
- VisitEnumSelect
- DiagTextPrinter
- DiagTextPrinter
- VisitMulti
- VisitText
- VisitPlaceholder
- VisitSelect
- VisitPlural
- VisitEnumSelect
- VisitDiff
- addInt
- parseModifier
- parseDiagText
- buildForDocumentation
- buildForEnum
- buildForDefinition
- isError
- isRemark
- isExemptAtStart
- isExemptAtEnd
- verifyDiagnosticWording
- EmitClangDiagsCompatIDs
- EmitClangDiagsInterface
- EmitClangDiagsEnums
- EmitClangDiagsDefs
- getDiagCategoryEnum
- emitDiagSubGroups
- emitDiagArrays
- emitDiagGroupNames
- emitAllDiagArrays
- emitDiagTable
- emitCategoryTable
- EmitClangDiagGroups
- EmitClangDiagsIndexName
- isRemarkGroup
- getDefaultSeverity
- getDefaultSeverities
- writeHeader
- writeDiagnosticText
Improve your Profiling and Debugging skills
Find out more