Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp
35230 views
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("DiagGroup");
48
for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) {
49
std::vector<Record*> SubGroups =
50
DiagGroups[i]->getValueAsListOfDefs("SubGroups");
51
for (unsigned j = 0, e = SubGroups.size(); j != e; ++j)
52
Mapping[SubGroups[j]].push_back(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("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(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>(R->getValueInit("Group"))) {
85
// Check the diagnostic's diag group for a category.
86
std::string CatName = getCategoryFromDiagGroup(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("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("");
106
CategoryIDs[""] = 0;
107
108
std::vector<Record*> Diags =
109
Records.getAllDerivedDefinitions("Diagnostic");
110
for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
111
std::string Category = getDiagnosticCategory(Diags[i], 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(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("GroupName") <
151
RHS->getValueAsString("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>(R->getValueInit("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("GroupName"));
169
DiagsInGroup[GroupName].DiagsInGroup.push_back(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("GroupName"))];
178
GI.GroupName = Group->getName();
179
GI.Defs.push_back(Group);
180
181
std::vector<Record*> SubGroups = Group->getValueAsListOfDefs("SubGroups");
182
for (unsigned j = 0, e = SubGroups.size(); j != e; ++j)
183
GI.SubGroups.push_back(
184
std::string(SubGroups[j]->getValueAsString("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>(Diag->getValueInit("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, SourceMgr::DK_Error,
217
Twine("group '") + Group.first +
218
"' is defined more than once");
219
First = false;
220
} else {
221
SrcMgr.PrintMessage(Loc, SourceMgr::DK_Note, "also defined here");
222
}
223
}
224
225
for (const Record *Diag : Group.second.DiagsInGroup) {
226
if (!cast<DefInit>(Diag->getValueInit("Group"))->getDef()->isAnonymous())
227
continue;
228
229
llvm::SMLoc Loc = Diag->getLoc().front();
230
if (First) {
231
SrcMgr.PrintMessage(Loc, SourceMgr::DK_Error,
232
Twine("group '") + Group.first +
233
"' is implicitly defined more than once");
234
First = false;
235
} else {
236
SrcMgr.PrintMessage(Loc, SourceMgr::DK_Note,
237
"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("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(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("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("DefaultSeverity")->getValueAsString("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("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(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(R) && isOffByDefault(R)) {
364
DiagsSet.insert(R);
365
if (DefInit *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) {
366
const Record *GroupRec = Group->getDef();
367
if (!isSubGroupOfGroup(GroupRec, "pedantic")) {
368
markGroup(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(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>(R->getValueInit("Group")))
385
if (groupInPedantic(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(R);
392
else {
393
DiagsInPedantic.get<RecordSet*>()->insert(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(Parents, [&](Record *R) { return groupInPedantic(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(Group);
420
else {
421
GroupsInPedantic.get<RecordSet*>()->insert(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("TextSubstitution")) {
589
EvaluatingRecordGuard Guard(&EvaluatingRecord, S);
590
Substitutions.try_emplace(
591
S->getName(), DiagText(*this, S->getValueAsString("Substitution")));
592
}
593
594
// Check that no diagnostic definitions have the same name as a
595
// substitution.
596
for (Record *Diag : Records.getAllDerivedDefinitions("Diagnostic")) {
597
StringRef Name = Diag->getName();
598
if (Substitutions.count(Name))
599
llvm::PrintFatalError(
600
Diag->getLoc(),
601
"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(S->Name);
612
if (It == Substitutions.end())
613
PrintFatalError("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(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, 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(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("Modifier value '" + std::to_string(Idx) +
754
"' is not valid for this mapping (has " +
755
std::to_string(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(K))
772
Out.push_back('\\');
773
Out.push_back(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>(OrigP)) {
808
ModifierMappingsType NewMappings =
809
getSubstitutionMappings(Sub, CurrentMappings);
810
return gatherNodes(Builder.getSubstitution(Sub), NewMappings, Pieces);
811
}
812
if (auto *MD = dyn_cast<MultiPiece>(OrigP)) {
813
for (Piece *Node : MD->Pieces)
814
gatherNodes(Node, CurrentMappings, Pieces);
815
return;
816
}
817
Pieces.push_back(std::make_pair(OrigP, CurrentMappings));
818
}
819
820
void VisitMulti(MultiPiece *P) {
821
if (P->Pieces.empty()) {
822
RST.push_back("");
823
return;
824
}
825
826
if (P->Pieces.size() == 1)
827
return Visit(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(P, 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(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(Start + Lines.size(), EmptyLinePrefix);
853
854
padToSameLength(Lines.begin(), 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(EmptyLinePrefix);
867
RST.insert(RST.begin() + Start, EmptyLinePrefix);
868
RST.insert(RST.end(), EmptyLinePrefix);
869
}
870
871
void VisitText(TextPiece *P) {
872
RST.push_back("");
873
auto &S = RST.back();
874
875
StringRef T = P->Text;
876
while (T.consume_front(" "))
877
RST.back() += " |nbsp| ";
878
879
std::string Suffix;
880
while (T.consume_back(" "))
881
Suffix += " |nbsp| ";
882
883
if (!T.empty()) {
884
S += ':';
885
S += P->Role;
886
S += ":`";
887
escapeRST(T, S);
888
S += '`';
889
}
890
891
S += Suffix;
892
}
893
894
void VisitPlaceholder(PlaceholderPiece *P) {
895
RST.push_back(std::string(":placeholder:`") +
896
char('A' + mapIndex(P->Index)) + "`");
897
}
898
899
void VisitSelect(SelectPiece *P) {
900
std::vector<size_t> SeparatorIndexes;
901
SeparatorIndexes.push_back(RST.size());
902
RST.emplace_back();
903
for (auto *O : P->Options) {
904
Visit(O);
905
SeparatorIndexes.push_back(RST.size());
906
RST.emplace_back();
907
}
908
909
makeTableRows(RST.begin() + SeparatorIndexes.front(),
910
RST.begin() + SeparatorIndexes.back() + 1);
911
for (size_t I : SeparatorIndexes)
912
makeRowSeparator(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(P->Parts[0]);
924
FirstOption.Pieces.push_back(&E);
925
FirstOption.Pieces.push_back(P->Parts[1]);
926
FirstOption.Pieces.push_back(&F);
927
FirstOption.Pieces.push_back(P->Parts[2]);
928
929
SelectPiece Select(MT_Diff);
930
Select.Options.push_back(&FirstOption);
931
Select.Options.push_back(P->Parts[3]);
932
933
VisitSelect(&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(Child);
948
}
949
void VisitText(TextPiece *P) { Result += P->Text; }
950
void VisitPlaceholder(PlaceholderPiece *P) {
951
Result += "%";
952
Result += getModifierName(P->Kind);
953
addInt(mapIndex(P->Index));
954
}
955
void VisitSelect(SelectPiece *P) {
956
Result += "%";
957
Result += getModifierName(P->ModKind);
958
if (P->ModKind == MT_Select) {
959
Result += "{";
960
for (auto *D : P->Options) {
961
Visit(D);
962
Result += '|';
963
}
964
if (!P->Options.empty())
965
Result.erase(--Result.end());
966
Result += '}';
967
}
968
addInt(mapIndex(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->OptionPrefixes[I]);
977
Visit(P->Options[I]);
978
Result += "|";
979
}
980
if (!P->Options.empty())
981
Result.erase(--Result.end());
982
Result += '}';
983
addInt(mapIndex(P->Index));
984
}
985
986
void VisitDiff(DiffPiece *P) {
987
Result += "%diff{";
988
Visit(P->Parts[0]);
989
Result += "$";
990
Visit(P->Parts[1]);
991
Result += "$";
992
Visit(P->Parts[2]);
993
Result += "|";
994
Visit(P->Parts[3]);
995
Result += "}";
996
addInt(mapIndex(P->Indexes[0]));
997
Result += ",";
998
addInt(mapIndex(P->Indexes[1]));
999
}
1000
1001
void addInt(int Val) { Result += std::to_string(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("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(StopSet, 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(New<TextPiece>(Text.slice(0, End), "diagtext"));
1035
Text = Text.slice(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("0123456789{");
1048
StringRef Modifier = Text.slice(0, ModLength);
1049
Text = Text.slice(ModLength, StringRef::npos);
1050
ModifierType ModType = llvm::StringSwitch<ModifierType>{Modifier}
1051
.Case("select", MT_Select)
1052
.Case("sub", MT_Sub)
1053
.Case("diff", MT_Diff)
1054
.Case("plural", MT_Plural)
1055
.Case("s", MT_S)
1056
.Case("ordinal", MT_Ordinal)
1057
.Case("q", MT_Q)
1058
.Case("objcclass", MT_ObjCClass)
1059
.Case("objcinstance", MT_ObjCInstance)
1060
.Case("", MT_Placeholder)
1061
.Default(MT_Unknown);
1062
1063
auto ExpectAndConsume = [&](StringRef Prefix) {
1064
if (!Text.consume_front(Prefix))
1065
Builder.PrintFatalError("expected '" + Prefix + "' while parsing %" +
1066
Modifier);
1067
};
1068
1069
switch (ModType) {
1070
case MT_Unknown:
1071
Builder.PrintFatalError("Unknown modifier type: " + Modifier);
1072
case MT_Select: {
1073
SelectPiece *Select = New<SelectPiece>(MT_Select);
1074
do {
1075
Text = Text.drop_front(); // '{' or '|'
1076
Select->Options.push_back(
1077
parseDiagText(Text, StopAt::PipeOrCloseBrace));
1078
assert(!Text.empty() && "malformed %select");
1079
} while (Text.front() == '|');
1080
ExpectAndConsume("}");
1081
Select->Index = parseModifier(Text);
1082
Parsed.push_back(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(':');
1090
if (End == StringRef::npos)
1091
Builder.PrintFatalError("expected ':' while parsing %plural");
1092
++End;
1093
assert(!Text.empty());
1094
Plural->OptionPrefixes.push_back(
1095
New<TextPiece>(Text.slice(0, End), "diagtext"));
1096
Text = Text.slice(End, StringRef::npos);
1097
Plural->Options.push_back(
1098
parseDiagText(Text, StopAt::PipeOrCloseBrace));
1099
assert(!Text.empty() && "malformed %plural");
1100
} while (Text.front() == '|');
1101
ExpectAndConsume("}");
1102
Plural->Index = parseModifier(Text);
1103
Parsed.push_back(Plural);
1104
continue;
1105
}
1106
case MT_Sub: {
1107
SubstitutionPiece *Sub = New<SubstitutionPiece>();
1108
ExpectAndConsume("{");
1109
size_t NameSize = Text.find_first_of('}');
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(0, NameSize).str();
1113
Text = Text.drop_front(NameSize);
1114
ExpectAndConsume("}");
1115
if (!Text.empty()) {
1116
while (true) {
1117
if (!isdigit(Text[0]))
1118
break;
1119
Sub->Modifiers.push_back(parseModifier(Text));
1120
if (!Text.consume_front(","))
1121
break;
1122
assert(!Text.empty() && isdigit(Text[0]) &&
1123
"expected another modifier");
1124
}
1125
}
1126
Parsed.push_back(Sub);
1127
continue;
1128
}
1129
case MT_Diff: {
1130
DiffPiece *Diff = New<DiffPiece>();
1131
ExpectAndConsume("{");
1132
Diff->Parts[0] = parseDiagText(Text, StopAt::Dollar);
1133
ExpectAndConsume("$");
1134
Diff->Parts[1] = parseDiagText(Text, StopAt::Dollar);
1135
ExpectAndConsume("$");
1136
Diff->Parts[2] = parseDiagText(Text, StopAt::PipeOrCloseBrace);
1137
ExpectAndConsume("|");
1138
Diff->Parts[3] = parseDiagText(Text, StopAt::PipeOrCloseBrace);
1139
ExpectAndConsume("}");
1140
Diff->Indexes[0] = parseModifier(Text);
1141
ExpectAndConsume(",");
1142
Diff->Indexes[1] = parseModifier(Text);
1143
Parsed.push_back(Diff);
1144
continue;
1145
}
1146
case MT_S: {
1147
SelectPiece *Select = New<SelectPiece>(ModType);
1148
Select->Options.push_back(New<TextPiece>(""));
1149
Select->Options.push_back(New<TextPiece>("s", "diagtext"));
1150
Select->Index = parseModifier(Text);
1151
Parsed.push_back(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(New<PlaceholderPiece>(ModType, parseModifier(Text)));
1160
continue;
1161
}
1162
}
1163
}
1164
1165
return New<MultiPiece>(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("Summary");
1173
1174
DiagText D(*this, Text);
1175
TextPiece *Prefix = D.New<TextPiece>(Severity, Severity);
1176
Prefix->Text += ": ";
1177
auto *MP = dyn_cast<MultiPiece>(D.Root);
1178
if (!MP) {
1179
MP = D.New<MultiPiece>();
1180
MP->Pieces.push_back(D.Root);
1181
D.Root = MP;
1182
}
1183
MP->Pieces.insert(MP->Pieces.begin(), Prefix);
1184
std::vector<std::string> Result;
1185
DiagTextDocPrinter{*this, Result}.Visit(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("Summary");
1192
DiagText D(*this, Text);
1193
std::string Result;
1194
DiagTextPrinter{*this, Result}.Visit(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("Class")->getName());
1207
return ClsName == "CLASS_ERROR";
1208
}
1209
1210
static bool isRemark(const Record &Diag) {
1211
const std::string &ClsName =
1212
std::string(Diag.getValueAsDef("Class")->getName());
1213
return ClsName == "CLASS_REMARK";
1214
}
1215
1216
// Presumes the text has been split at the first whitespace or hyphen.
1217
static bool isExemptAtStart(StringRef Text) {
1218
// Fast path, the first character is lowercase or not alphanumeric.
1219
if (Text.empty() || isLower(Text[0]) || !isAlnum(Text[0]))
1220
return true;
1221
1222
// If the text is all uppercase (or numbers, +, or _), then we assume it's an
1223
// acronym and that's allowed. This covers cases like ISO, C23, C++14, and
1224
// OBJECT_MODE. However, if there's only a single letter other than "C", we
1225
// do not exempt it so that we catch a case like "A really bad idea" while
1226
// still allowing a case like "C does not allow...".
1227
if (llvm::all_of(Text, [](char C) {
1228
return isUpper(C) || isDigit(C) || C == '+' || C == '_';
1229
}))
1230
return Text.size() > 1 || Text[0] == 'C';
1231
1232
// Otherwise, there are a few other exemptions.
1233
return StringSwitch<bool>(Text)
1234
.Case("AddressSanitizer", true)
1235
.Case("CFString", true)
1236
.Case("Clang", true)
1237
.Case("Fuchsia", true)
1238
.Case("GNUstep", true)
1239
.Case("IBOutletCollection", true)
1240
.Case("Microsoft", true)
1241
.Case("Neon", true)
1242
.StartsWith("NSInvocation", true) // NSInvocation, NSInvocation's
1243
.Case("Objective", true) // Objective-C (hyphen is a word boundary)
1244
.Case("OpenACC", true)
1245
.Case("OpenCL", true)
1246
.Case("OpenMP", true)
1247
.Case("Pascal", true)
1248
.Case("Swift", true)
1249
.Case("Unicode", true)
1250
.Case("Vulkan", true)
1251
.Case("WebAssembly", true)
1252
.Default(false);
1253
}
1254
1255
// Does not presume the text has been split at all.
1256
static bool isExemptAtEnd(StringRef Text) {
1257
// Rather than come up with a list of characters that are allowed, we go the
1258
// other way and look only for characters that are not allowed.
1259
switch (Text.back()) {
1260
default:
1261
return true;
1262
case '?':
1263
// Explicitly allowed to support "; did you mean?".
1264
return true;
1265
case '.':
1266
case '!':
1267
return false;
1268
}
1269
}
1270
1271
static void verifyDiagnosticWording(const Record &Diag) {
1272
StringRef FullDiagText = Diag.getValueAsString("Summary");
1273
1274
auto DiagnoseStart = [&](StringRef Text) {
1275
// Verify that the text does not start with a capital letter, except for
1276
// special cases that are exempt like ISO and C++. Find the first word
1277
// by looking for a word breaking character.
1278
char Separators[] = {' ', '-', ',', '}'};
1279
auto Iter = std::find_first_of(
1280
Text.begin(), Text.end(), std::begin(Separators), std::end(Separators));
1281
1282
StringRef First = Text.substr(0, Iter - Text.begin());
1283
if (!isExemptAtStart(First)) {
1284
PrintError(&Diag,
1285
"Diagnostics should not start with a capital letter; '" +
1286
First + "' is invalid");
1287
}
1288
};
1289
1290
auto DiagnoseEnd = [&](StringRef Text) {
1291
// Verify that the text does not end with punctuation like '.' or '!'.
1292
if (!isExemptAtEnd(Text)) {
1293
PrintError(&Diag, "Diagnostics should not end with punctuation; '" +
1294
Text.substr(Text.size() - 1, 1) + "' is invalid");
1295
}
1296
};
1297
1298
// If the diagnostic starts with %select, look through it to see whether any
1299
// of the options will cause a problem.
1300
if (FullDiagText.starts_with("%select{")) {
1301
// Do a balanced delimiter scan from the start of the text to find the
1302
// closing '}', skipping intermediary {} pairs.
1303
1304
size_t BraceCount = 1;
1305
constexpr size_t PercentSelectBraceLen = sizeof("%select{") - 1;
1306
auto Iter = FullDiagText.begin() + PercentSelectBraceLen;
1307
for (auto End = FullDiagText.end(); Iter != End; ++Iter) {
1308
char Ch = *Iter;
1309
if (Ch == '{')
1310
++BraceCount;
1311
else if (Ch == '}')
1312
--BraceCount;
1313
if (!BraceCount)
1314
break;
1315
}
1316
// Defending against a malformed diagnostic string.
1317
if (BraceCount != 0)
1318
return;
1319
1320
StringRef SelectText =
1321
FullDiagText.substr(PercentSelectBraceLen, Iter - FullDiagText.begin() -
1322
PercentSelectBraceLen);
1323
SmallVector<StringRef, 4> SelectPieces;
1324
SelectText.split(SelectPieces, '|');
1325
1326
// Walk over all of the individual pieces of select text to see if any of
1327
// them start with an invalid character. If any of the select pieces is
1328
// empty, we need to look at the first word after the %select to see
1329
// whether that is invalid or not. If all of the pieces are fine, then we
1330
// don't need to check anything else about the start of the diagnostic.
1331
bool CheckSecondWord = false;
1332
for (StringRef Piece : SelectPieces) {
1333
if (Piece.empty())
1334
CheckSecondWord = true;
1335
else
1336
DiagnoseStart(Piece);
1337
}
1338
1339
if (CheckSecondWord) {
1340
// There was an empty select piece, so we need to check the second
1341
// word. This catches situations like '%select{|fine}0 Not okay'. Add
1342
// two to account for the closing curly brace and the number after it.
1343
StringRef AfterSelect =
1344
FullDiagText.substr(Iter - FullDiagText.begin() + 2).ltrim();
1345
DiagnoseStart(AfterSelect);
1346
}
1347
} else {
1348
// If the start of the diagnostic is not %select, we can check the first
1349
// word and be done with it.
1350
DiagnoseStart(FullDiagText);
1351
}
1352
1353
// If the last character in the diagnostic is a number preceded by a }, scan
1354
// backwards to see if this is for a %select{...}0. If it is, we need to look
1355
// at each piece to see whether it ends in punctuation or not.
1356
bool StillNeedToDiagEnd = true;
1357
if (isDigit(FullDiagText.back()) && *(FullDiagText.end() - 2) == '}') {
1358
// Scan backwards to find the opening curly brace.
1359
size_t BraceCount = 1;
1360
auto Iter = FullDiagText.end() - sizeof("}0");
1361
for (auto End = FullDiagText.begin(); Iter != End; --Iter) {
1362
char Ch = *Iter;
1363
if (Ch == '}')
1364
++BraceCount;
1365
else if (Ch == '{')
1366
--BraceCount;
1367
if (!BraceCount)
1368
break;
1369
}
1370
// Defending against a malformed diagnostic string.
1371
if (BraceCount != 0)
1372
return;
1373
1374
// Continue the backwards scan to find the word before the '{' to see if it
1375
// is 'select'.
1376
constexpr size_t SelectLen = sizeof("select") - 1;
1377
bool IsSelect =
1378
(FullDiagText.substr(Iter - SelectLen - FullDiagText.begin(),
1379
SelectLen) == "select");
1380
if (IsSelect) {
1381
// Gather the content between the {} for the select in question so we can
1382
// split it into pieces.
1383
StillNeedToDiagEnd = false; // No longer need to handle the end.
1384
StringRef SelectText =
1385
FullDiagText.substr(Iter - FullDiagText.begin() + /*{*/ 1,
1386
FullDiagText.end() - Iter - /*pos before }0*/ 3);
1387
SmallVector<StringRef, 4> SelectPieces;
1388
SelectText.split(SelectPieces, '|');
1389
for (StringRef Piece : SelectPieces) {
1390
// Not worrying about a situation like: "this is bar. %select{foo|}0".
1391
if (!Piece.empty())
1392
DiagnoseEnd(Piece);
1393
}
1394
}
1395
}
1396
1397
// If we didn't already cover the diagnostic because of a %select, handle it
1398
// now.
1399
if (StillNeedToDiagEnd)
1400
DiagnoseEnd(FullDiagText);
1401
1402
// FIXME: This could also be improved by looking for instances of clang or
1403
// gcc in the diagnostic and recommend Clang or GCC instead. However, this
1404
// runs into odd situations like [[clang::warn_unused_result]],
1405
// #pragma clang, or --unwindlib=libgcc.
1406
}
1407
1408
/// ClangDiagsDefsEmitter - The top-level class emits .def files containing
1409
/// declarations of Clang diagnostics.
1410
void clang::EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS,
1411
const std::string &Component) {
1412
// Write the #if guard
1413
if (!Component.empty()) {
1414
std::string ComponentName = StringRef(Component).upper();
1415
OS << "#ifdef " << ComponentName << "START\n";
1416
OS << "__" << ComponentName << "START = DIAG_START_" << ComponentName
1417
<< ",\n";
1418
OS << "#undef " << ComponentName << "START\n";
1419
OS << "#endif\n\n";
1420
}
1421
1422
DiagnosticTextBuilder DiagTextBuilder(Records);
1423
1424
std::vector<Record *> Diags = Records.getAllDerivedDefinitions("Diagnostic");
1425
1426
std::vector<Record*> DiagGroups
1427
= Records.getAllDerivedDefinitions("DiagGroup");
1428
1429
std::map<std::string, GroupInfo> DiagsInGroup;
1430
groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
1431
1432
DiagCategoryIDMap CategoryIDs(Records);
1433
DiagGroupParentMap DGParentMap(Records);
1434
1435
// Compute the set of diagnostics that are in -Wpedantic.
1436
RecordSet DiagsInPedantic;
1437
InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
1438
inferPedantic.compute(&DiagsInPedantic, (RecordVec*)nullptr);
1439
1440
for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
1441
const Record &R = *Diags[i];
1442
1443
// Check if this is an error that is accidentally in a warning
1444
// group.
1445
if (isError(R)) {
1446
if (DefInit *Group = dyn_cast<DefInit>(R.getValueInit("Group"))) {
1447
const Record *GroupRec = Group->getDef();
1448
const std::string &GroupName =
1449
std::string(GroupRec->getValueAsString("GroupName"));
1450
PrintFatalError(R.getLoc(), "Error " + R.getName() +
1451
" cannot be in a warning group [" + GroupName + "]");
1452
}
1453
}
1454
1455
// Check that all remarks have an associated diagnostic group.
1456
if (isRemark(R)) {
1457
if (!isa<DefInit>(R.getValueInit("Group"))) {
1458
PrintFatalError(R.getLoc(), "Error " + R.getName() +
1459
" not in any diagnostic group");
1460
}
1461
}
1462
1463
// Filter by component.
1464
if (!Component.empty() && Component != R.getValueAsString("Component"))
1465
continue;
1466
1467
// Validate diagnostic wording for common issues.
1468
verifyDiagnosticWording(R);
1469
1470
OS << "DIAG(" << R.getName() << ", ";
1471
OS << R.getValueAsDef("Class")->getName();
1472
OS << ", (unsigned)diag::Severity::"
1473
<< R.getValueAsDef("DefaultSeverity")->getValueAsString("Name");
1474
1475
// Description string.
1476
OS << ", \"";
1477
OS.write_escaped(DiagTextBuilder.buildForDefinition(&R)) << '"';
1478
1479
// Warning group associated with the diagnostic. This is stored as an index
1480
// into the alphabetically sorted warning group table.
1481
if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) {
1482
std::map<std::string, GroupInfo>::iterator I = DiagsInGroup.find(
1483
std::string(DI->getDef()->getValueAsString("GroupName")));
1484
assert(I != DiagsInGroup.end());
1485
OS << ", " << I->second.IDNo;
1486
} else if (DiagsInPedantic.count(&R)) {
1487
std::map<std::string, GroupInfo>::iterator I =
1488
DiagsInGroup.find("pedantic");
1489
assert(I != DiagsInGroup.end() && "pedantic group not defined");
1490
OS << ", " << I->second.IDNo;
1491
} else {
1492
OS << ", 0";
1493
}
1494
1495
// SFINAE response.
1496
OS << ", " << R.getValueAsDef("SFINAE")->getName();
1497
1498
// Default warning has no Werror bit.
1499
if (R.getValueAsBit("WarningNoWerror"))
1500
OS << ", true";
1501
else
1502
OS << ", false";
1503
1504
if (R.getValueAsBit("ShowInSystemHeader"))
1505
OS << ", true";
1506
else
1507
OS << ", false";
1508
1509
if (R.getValueAsBit("ShowInSystemMacro"))
1510
OS << ", true";
1511
else
1512
OS << ", false";
1513
1514
if (R.getValueAsBit("Deferrable"))
1515
OS << ", true";
1516
else
1517
OS << ", false";
1518
1519
// Category number.
1520
OS << ", " << CategoryIDs.getID(getDiagnosticCategory(&R, DGParentMap));
1521
OS << ")\n";
1522
}
1523
}
1524
1525
//===----------------------------------------------------------------------===//
1526
// Warning Group Tables generation
1527
//===----------------------------------------------------------------------===//
1528
1529
static std::string getDiagCategoryEnum(llvm::StringRef name) {
1530
if (name.empty())
1531
return "DiagCat_None";
1532
SmallString<256> enumName = llvm::StringRef("DiagCat_");
1533
for (llvm::StringRef::iterator I = name.begin(), E = name.end(); I != E; ++I)
1534
enumName += isalnum(*I) ? *I : '_';
1535
return std::string(enumName);
1536
}
1537
1538
/// Emit the array of diagnostic subgroups.
1539
///
1540
/// The array of diagnostic subgroups contains for each group a list of its
1541
/// subgroups. The individual lists are separated by '-1'. Groups with no
1542
/// subgroups are skipped.
1543
///
1544
/// \code
1545
/// static const int16_t DiagSubGroups[] = {
1546
/// /* Empty */ -1,
1547
/// /* DiagSubGroup0 */ 142, -1,
1548
/// /* DiagSubGroup13 */ 265, 322, 399, -1
1549
/// }
1550
/// \endcode
1551
///
1552
static void emitDiagSubGroups(std::map<std::string, GroupInfo> &DiagsInGroup,
1553
RecordVec &GroupsInPedantic, raw_ostream &OS) {
1554
OS << "static const int16_t DiagSubGroups[] = {\n"
1555
<< " /* Empty */ -1,\n";
1556
for (auto const &I : DiagsInGroup) {
1557
const bool IsPedantic = I.first == "pedantic";
1558
1559
const std::vector<std::string> &SubGroups = I.second.SubGroups;
1560
if (!SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty())) {
1561
OS << " /* DiagSubGroup" << I.second.IDNo << " */ ";
1562
for (auto const &SubGroup : SubGroups) {
1563
std::map<std::string, GroupInfo>::const_iterator RI =
1564
DiagsInGroup.find(SubGroup);
1565
assert(RI != DiagsInGroup.end() && "Referenced without existing?");
1566
OS << RI->second.IDNo << ", ";
1567
}
1568
// Emit the groups implicitly in "pedantic".
1569
if (IsPedantic) {
1570
for (auto const &Group : GroupsInPedantic) {
1571
const std::string &GroupName =
1572
std::string(Group->getValueAsString("GroupName"));
1573
std::map<std::string, GroupInfo>::const_iterator RI =
1574
DiagsInGroup.find(GroupName);
1575
assert(RI != DiagsInGroup.end() && "Referenced without existing?");
1576
OS << RI->second.IDNo << ", ";
1577
}
1578
}
1579
1580
OS << "-1,\n";
1581
}
1582
}
1583
OS << "};\n\n";
1584
}
1585
1586
/// Emit the list of diagnostic arrays.
1587
///
1588
/// This data structure is a large array that contains itself arrays of varying
1589
/// size. Each array represents a list of diagnostics. The different arrays are
1590
/// separated by the value '-1'.
1591
///
1592
/// \code
1593
/// static const int16_t DiagArrays[] = {
1594
/// /* Empty */ -1,
1595
/// /* DiagArray1 */ diag::warn_pragma_message,
1596
/// -1,
1597
/// /* DiagArray2 */ diag::warn_abs_too_small,
1598
/// diag::warn_unsigned_abs,
1599
/// diag::warn_wrong_absolute_value_type,
1600
/// -1
1601
/// };
1602
/// \endcode
1603
///
1604
static void emitDiagArrays(std::map<std::string, GroupInfo> &DiagsInGroup,
1605
RecordVec &DiagsInPedantic, raw_ostream &OS) {
1606
OS << "static const int16_t DiagArrays[] = {\n"
1607
<< " /* Empty */ -1,\n";
1608
for (auto const &I : DiagsInGroup) {
1609
const bool IsPedantic = I.first == "pedantic";
1610
1611
const std::vector<const Record *> &V = I.second.DiagsInGroup;
1612
if (!V.empty() || (IsPedantic && !DiagsInPedantic.empty())) {
1613
OS << " /* DiagArray" << I.second.IDNo << " */ ";
1614
for (auto *Record : V)
1615
OS << "diag::" << Record->getName() << ", ";
1616
// Emit the diagnostics implicitly in "pedantic".
1617
if (IsPedantic) {
1618
for (auto const &Diag : DiagsInPedantic)
1619
OS << "diag::" << Diag->getName() << ", ";
1620
}
1621
OS << "-1,\n";
1622
}
1623
}
1624
OS << "};\n\n";
1625
}
1626
1627
/// Emit a list of group names.
1628
///
1629
/// This creates a long string which by itself contains a list of pascal style
1630
/// strings, which consist of a length byte directly followed by the string.
1631
///
1632
/// \code
1633
/// static const char DiagGroupNames[] = {
1634
/// \000\020#pragma-messages\t#warnings\020CFString-literal"
1635
/// };
1636
/// \endcode
1637
static void emitDiagGroupNames(StringToOffsetTable &GroupNames,
1638
raw_ostream &OS) {
1639
OS << "static const char DiagGroupNames[] = {\n";
1640
GroupNames.EmitString(OS);
1641
OS << "};\n\n";
1642
}
1643
1644
/// Emit diagnostic arrays and related data structures.
1645
///
1646
/// This creates the actual diagnostic array, an array of diagnostic subgroups
1647
/// and an array of subgroup names.
1648
///
1649
/// \code
1650
/// #ifdef GET_DIAG_ARRAYS
1651
/// static const int16_t DiagArrays[];
1652
/// static const int16_t DiagSubGroups[];
1653
/// static const char DiagGroupNames[];
1654
/// #endif
1655
/// \endcode
1656
static void emitAllDiagArrays(std::map<std::string, GroupInfo> &DiagsInGroup,
1657
RecordVec &DiagsInPedantic,
1658
RecordVec &GroupsInPedantic,
1659
StringToOffsetTable &GroupNames,
1660
raw_ostream &OS) {
1661
OS << "\n#ifdef GET_DIAG_ARRAYS\n";
1662
emitDiagArrays(DiagsInGroup, DiagsInPedantic, OS);
1663
emitDiagSubGroups(DiagsInGroup, GroupsInPedantic, OS);
1664
emitDiagGroupNames(GroupNames, OS);
1665
OS << "#endif // GET_DIAG_ARRAYS\n\n";
1666
}
1667
1668
/// Emit diagnostic table.
1669
///
1670
/// The table is sorted by the name of the diagnostic group. Each element
1671
/// consists of the name of the diagnostic group (given as offset in the
1672
/// group name table), a reference to a list of diagnostics (optional) and a
1673
/// reference to a set of subgroups (optional).
1674
///
1675
/// \code
1676
/// #ifdef GET_DIAG_TABLE
1677
/// {/* abi */ 159, /* DiagArray11 */ 19, /* Empty */ 0},
1678
/// {/* aggregate-return */ 180, /* Empty */ 0, /* Empty */ 0},
1679
/// {/* all */ 197, /* Empty */ 0, /* DiagSubGroup13 */ 3},
1680
/// {/* deprecated */ 1981,/* DiagArray1 */ 348, /* DiagSubGroup3 */ 9},
1681
/// #endif
1682
/// \endcode
1683
static void emitDiagTable(std::map<std::string, GroupInfo> &DiagsInGroup,
1684
RecordVec &DiagsInPedantic,
1685
RecordVec &GroupsInPedantic,
1686
StringToOffsetTable &GroupNames, raw_ostream &OS) {
1687
unsigned MaxLen = 0;
1688
1689
for (auto const &I: DiagsInGroup)
1690
MaxLen = std::max(MaxLen, (unsigned)I.first.size());
1691
1692
OS << "\n#ifdef DIAG_ENTRY\n";
1693
unsigned SubGroupIndex = 1, DiagArrayIndex = 1;
1694
for (auto const &I: DiagsInGroup) {
1695
// Group option string.
1696
OS << "DIAG_ENTRY(";
1697
OS << I.second.GroupName << " /* ";
1698
1699
if (I.first.find_first_not_of("abcdefghijklmnopqrstuvwxyz"
1700
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1701
"0123456789!@#$%^*-+=:?") !=
1702
std::string::npos)
1703
PrintFatalError("Invalid character in diagnostic group '" + I.first +
1704
"'");
1705
OS << I.first << " */, ";
1706
// Store a pascal-style length byte at the beginning of the string.
1707
std::string Name = char(I.first.size()) + I.first;
1708
OS << GroupNames.GetOrAddStringOffset(Name, false) << ", ";
1709
1710
// Special handling for 'pedantic'.
1711
const bool IsPedantic = I.first == "pedantic";
1712
1713
// Diagnostics in the group.
1714
const std::vector<const Record *> &V = I.second.DiagsInGroup;
1715
const bool hasDiags =
1716
!V.empty() || (IsPedantic && !DiagsInPedantic.empty());
1717
if (hasDiags) {
1718
OS << "/* DiagArray" << I.second.IDNo << " */ " << DiagArrayIndex
1719
<< ", ";
1720
if (IsPedantic)
1721
DiagArrayIndex += DiagsInPedantic.size();
1722
DiagArrayIndex += V.size() + 1;
1723
} else {
1724
OS << "0, ";
1725
}
1726
1727
// Subgroups.
1728
const std::vector<std::string> &SubGroups = I.second.SubGroups;
1729
const bool hasSubGroups =
1730
!SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty());
1731
if (hasSubGroups) {
1732
OS << "/* DiagSubGroup" << I.second.IDNo << " */ " << SubGroupIndex
1733
<< ", ";
1734
if (IsPedantic)
1735
SubGroupIndex += GroupsInPedantic.size();
1736
SubGroupIndex += SubGroups.size() + 1;
1737
} else {
1738
OS << "0, ";
1739
}
1740
1741
std::string Documentation = I.second.Defs.back()
1742
->getValue("Documentation")
1743
->getValue()
1744
->getAsUnquotedString();
1745
1746
OS << "R\"(" << StringRef(Documentation).trim() << ")\"";
1747
1748
OS << ")\n";
1749
}
1750
OS << "#endif // DIAG_ENTRY\n\n";
1751
}
1752
1753
/// Emit the table of diagnostic categories.
1754
///
1755
/// The table has the form of macro calls that have two parameters. The
1756
/// category's name as well as an enum that represents the category. The
1757
/// table can be used by defining the macro 'CATEGORY' and including this
1758
/// table right after.
1759
///
1760
/// \code
1761
/// #ifdef GET_CATEGORY_TABLE
1762
/// CATEGORY("Semantic Issue", DiagCat_Semantic_Issue)
1763
/// CATEGORY("Lambda Issue", DiagCat_Lambda_Issue)
1764
/// #endif
1765
/// \endcode
1766
static void emitCategoryTable(RecordKeeper &Records, raw_ostream &OS) {
1767
DiagCategoryIDMap CategoriesByID(Records);
1768
OS << "\n#ifdef GET_CATEGORY_TABLE\n";
1769
for (auto const &C : CategoriesByID)
1770
OS << "CATEGORY(\"" << C << "\", " << getDiagCategoryEnum(C) << ")\n";
1771
OS << "#endif // GET_CATEGORY_TABLE\n\n";
1772
}
1773
1774
void clang::EmitClangDiagGroups(RecordKeeper &Records, raw_ostream &OS) {
1775
// Compute a mapping from a DiagGroup to all of its parents.
1776
DiagGroupParentMap DGParentMap(Records);
1777
1778
std::vector<Record *> Diags = Records.getAllDerivedDefinitions("Diagnostic");
1779
1780
std::vector<Record *> DiagGroups =
1781
Records.getAllDerivedDefinitions("DiagGroup");
1782
1783
std::map<std::string, GroupInfo> DiagsInGroup;
1784
groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
1785
1786
// All extensions are implicitly in the "pedantic" group. Record the
1787
// implicit set of groups in the "pedantic" group, and use this information
1788
// later when emitting the group information for Pedantic.
1789
RecordVec DiagsInPedantic;
1790
RecordVec GroupsInPedantic;
1791
InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
1792
inferPedantic.compute(&DiagsInPedantic, &GroupsInPedantic);
1793
1794
StringToOffsetTable GroupNames;
1795
for (std::map<std::string, GroupInfo>::const_iterator
1796
I = DiagsInGroup.begin(),
1797
E = DiagsInGroup.end();
1798
I != E; ++I) {
1799
// Store a pascal-style length byte at the beginning of the string.
1800
std::string Name = char(I->first.size()) + I->first;
1801
GroupNames.GetOrAddStringOffset(Name, false);
1802
}
1803
1804
emitAllDiagArrays(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames,
1805
OS);
1806
emitDiagTable(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames,
1807
OS);
1808
emitCategoryTable(Records, OS);
1809
}
1810
1811
//===----------------------------------------------------------------------===//
1812
// Diagnostic name index generation
1813
//===----------------------------------------------------------------------===//
1814
1815
namespace {
1816
struct RecordIndexElement
1817
{
1818
RecordIndexElement() {}
1819
explicit RecordIndexElement(Record const &R)
1820
: Name(std::string(R.getName())) {}
1821
1822
std::string Name;
1823
};
1824
} // end anonymous namespace.
1825
1826
void clang::EmitClangDiagsIndexName(RecordKeeper &Records, raw_ostream &OS) {
1827
const std::vector<Record*> &Diags =
1828
Records.getAllDerivedDefinitions("Diagnostic");
1829
1830
std::vector<RecordIndexElement> Index;
1831
Index.reserve(Diags.size());
1832
for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
1833
const Record &R = *(Diags[i]);
1834
Index.push_back(RecordIndexElement(R));
1835
}
1836
1837
llvm::sort(Index,
1838
[](const RecordIndexElement &Lhs, const RecordIndexElement &Rhs) {
1839
return Lhs.Name < Rhs.Name;
1840
});
1841
1842
for (unsigned i = 0, e = Index.size(); i != e; ++i) {
1843
const RecordIndexElement &R = Index[i];
1844
1845
OS << "DIAG_NAME_INDEX(" << R.Name << ")\n";
1846
}
1847
}
1848
1849
//===----------------------------------------------------------------------===//
1850
// Diagnostic documentation generation
1851
//===----------------------------------------------------------------------===//
1852
1853
namespace docs {
1854
namespace {
1855
1856
bool isRemarkGroup(const Record *DiagGroup,
1857
const std::map<std::string, GroupInfo> &DiagsInGroup) {
1858
bool AnyRemarks = false, AnyNonRemarks = false;
1859
1860
std::function<void(StringRef)> Visit = [&](StringRef GroupName) {
1861
auto &GroupInfo = DiagsInGroup.find(std::string(GroupName))->second;
1862
for (const Record *Diag : GroupInfo.DiagsInGroup)
1863
(isRemark(*Diag) ? AnyRemarks : AnyNonRemarks) = true;
1864
for (const auto &Name : GroupInfo.SubGroups)
1865
Visit(Name);
1866
};
1867
Visit(DiagGroup->getValueAsString("GroupName"));
1868
1869
if (AnyRemarks && AnyNonRemarks)
1870
PrintFatalError(
1871
DiagGroup->getLoc(),
1872
"Diagnostic group contains both remark and non-remark diagnostics");
1873
return AnyRemarks;
1874
}
1875
1876
std::string getDefaultSeverity(const Record *Diag) {
1877
return std::string(
1878
Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name"));
1879
}
1880
1881
std::set<std::string>
1882
getDefaultSeverities(const Record *DiagGroup,
1883
const std::map<std::string, GroupInfo> &DiagsInGroup) {
1884
std::set<std::string> States;
1885
1886
std::function<void(StringRef)> Visit = [&](StringRef GroupName) {
1887
auto &GroupInfo = DiagsInGroup.find(std::string(GroupName))->second;
1888
for (const Record *Diag : GroupInfo.DiagsInGroup)
1889
States.insert(getDefaultSeverity(Diag));
1890
for (const auto &Name : GroupInfo.SubGroups)
1891
Visit(Name);
1892
};
1893
Visit(DiagGroup->getValueAsString("GroupName"));
1894
return States;
1895
}
1896
1897
void writeHeader(StringRef Str, raw_ostream &OS, char Kind = '-') {
1898
OS << Str << "\n" << std::string(Str.size(), Kind) << "\n";
1899
}
1900
1901
void writeDiagnosticText(DiagnosticTextBuilder &Builder, const Record *R,
1902
StringRef Role, raw_ostream &OS) {
1903
StringRef Text = R->getValueAsString("Summary");
1904
if (Text == "%0")
1905
OS << "The text of this diagnostic is not controlled by Clang.\n\n";
1906
else {
1907
std::vector<std::string> Out = Builder.buildForDocumentation(Role, R);
1908
for (auto &Line : Out)
1909
OS << Line << "\n";
1910
OS << "\n";
1911
}
1912
}
1913
1914
} // namespace
1915
} // namespace docs
1916
1917
void clang::EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS) {
1918
using namespace docs;
1919
1920
// Get the documentation introduction paragraph.
1921
const Record *Documentation = Records.getDef("GlobalDocumentation");
1922
if (!Documentation) {
1923
PrintFatalError("The Documentation top-level definition is missing, "
1924
"no documentation will be generated.");
1925
return;
1926
}
1927
1928
OS << Documentation->getValueAsString("Intro") << "\n";
1929
1930
DiagnosticTextBuilder Builder(Records);
1931
1932
std::vector<Record*> Diags =
1933
Records.getAllDerivedDefinitions("Diagnostic");
1934
1935
std::vector<Record*> DiagGroups =
1936
Records.getAllDerivedDefinitions("DiagGroup");
1937
llvm::sort(DiagGroups, diagGroupBeforeByName);
1938
1939
DiagGroupParentMap DGParentMap(Records);
1940
1941
std::map<std::string, GroupInfo> DiagsInGroup;
1942
groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
1943
1944
// Compute the set of diagnostics that are in -Wpedantic.
1945
{
1946
RecordSet DiagsInPedanticSet;
1947
RecordSet GroupsInPedanticSet;
1948
InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
1949
inferPedantic.compute(&DiagsInPedanticSet, &GroupsInPedanticSet);
1950
auto &PedDiags = DiagsInGroup["pedantic"];
1951
// Put the diagnostics into a deterministic order.
1952
RecordVec DiagsInPedantic(DiagsInPedanticSet.begin(),
1953
DiagsInPedanticSet.end());
1954
RecordVec GroupsInPedantic(GroupsInPedanticSet.begin(),
1955
GroupsInPedanticSet.end());
1956
llvm::sort(DiagsInPedantic, beforeThanCompare);
1957
llvm::sort(GroupsInPedantic, beforeThanCompare);
1958
PedDiags.DiagsInGroup.insert(PedDiags.DiagsInGroup.end(),
1959
DiagsInPedantic.begin(),
1960
DiagsInPedantic.end());
1961
for (auto *Group : GroupsInPedantic)
1962
PedDiags.SubGroups.push_back(
1963
std::string(Group->getValueAsString("GroupName")));
1964
}
1965
1966
// FIXME: Write diagnostic categories and link to diagnostic groups in each.
1967
1968
// Write out the diagnostic groups.
1969
for (const Record *G : DiagGroups) {
1970
bool IsRemarkGroup = isRemarkGroup(G, DiagsInGroup);
1971
auto &GroupInfo =
1972
DiagsInGroup[std::string(G->getValueAsString("GroupName"))];
1973
bool IsSynonym = GroupInfo.DiagsInGroup.empty() &&
1974
GroupInfo.SubGroups.size() == 1;
1975
1976
writeHeader(((IsRemarkGroup ? "-R" : "-W") +
1977
G->getValueAsString("GroupName")).str(),
1978
OS);
1979
1980
if (!IsSynonym) {
1981
// FIXME: Ideally, all the diagnostics in a group should have the same
1982
// default state, but that is not currently the case.
1983
auto DefaultSeverities = getDefaultSeverities(G, DiagsInGroup);
1984
if (!DefaultSeverities.empty() && !DefaultSeverities.count("Ignored")) {
1985
bool AnyNonErrors = DefaultSeverities.count("Warning") ||
1986
DefaultSeverities.count("Remark");
1987
if (!AnyNonErrors)
1988
OS << "This diagnostic is an error by default, but the flag ``-Wno-"
1989
<< G->getValueAsString("GroupName") << "`` can be used to disable "
1990
<< "the error.\n\n";
1991
else
1992
OS << "This diagnostic is enabled by default.\n\n";
1993
} else if (DefaultSeverities.size() > 1) {
1994
OS << "Some of the diagnostics controlled by this flag are enabled "
1995
<< "by default.\n\n";
1996
}
1997
}
1998
1999
if (!GroupInfo.SubGroups.empty()) {
2000
if (IsSynonym)
2001
OS << "Synonym for ";
2002
else if (GroupInfo.DiagsInGroup.empty())
2003
OS << "Controls ";
2004
else
2005
OS << "Also controls ";
2006
2007
bool First = true;
2008
llvm::sort(GroupInfo.SubGroups);
2009
for (const auto &Name : GroupInfo.SubGroups) {
2010
if (!First) OS << ", ";
2011
OS << "`" << (IsRemarkGroup ? "-R" : "-W") << Name << "`_";
2012
First = false;
2013
}
2014
OS << ".\n\n";
2015
}
2016
2017
if (!GroupInfo.DiagsInGroup.empty()) {
2018
OS << "**Diagnostic text:**\n\n";
2019
for (const Record *D : GroupInfo.DiagsInGroup) {
2020
auto Severity = getDefaultSeverity(D);
2021
Severity[0] = tolower(Severity[0]);
2022
if (Severity == "ignored")
2023
Severity = IsRemarkGroup ? "remark" : "warning";
2024
2025
writeDiagnosticText(Builder, D, Severity, OS);
2026
}
2027
}
2028
2029
auto Doc = G->getValueAsString("Documentation");
2030
if (!Doc.empty())
2031
OS << Doc;
2032
else if (GroupInfo.SubGroups.empty() && GroupInfo.DiagsInGroup.empty())
2033
OS << "This diagnostic flag exists for GCC compatibility, and has no "
2034
"effect in Clang.\n";
2035
OS << "\n";
2036
}
2037
}
2038
2039