2255 lines · cpp
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-exception6//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>34using namespace llvm;35 36//===----------------------------------------------------------------------===//37// Diagnostic category computation code.38//===----------------------------------------------------------------------===//39 40namespace {41class DiagGroupParentMap {42 const RecordKeeper &Records;43 std::map<const Record *, std::vector<const Record *>> Mapping;44 45public:46 DiagGroupParentMap(const RecordKeeper &records) : Records(records) {47 for (const Record *Group : Records.getAllDerivedDefinitions("DiagGroup"))48 for (const Record *SubGroup : Group->getValueAsListOfDefs("SubGroups"))49 Mapping[SubGroup].push_back(Group);50 }51 52 ArrayRef<const Record *> getParents(const Record *Group) {53 return Mapping[Group];54 }55};56} // end anonymous namespace.57 58static StringRef59getCategoryFromDiagGroup(const Record *Group,60 DiagGroupParentMap &DiagGroupParents) {61 // If the DiagGroup has a category, return it.62 StringRef CatName = Group->getValueAsString("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(Parent, DiagGroupParents);69 if (!CatName.empty()) return CatName;70 }71 return "";72}73 74/// getDiagnosticCategory - Return the category that the specified diagnostic75/// lives in.76static 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>(R->getValueInit("Group"))) {80 // Check the diagnostic's diag group for a category.81 StringRef CatName =82 getCategoryFromDiagGroup(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("CategoryName");88}89 90namespace {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("");102 CategoryIDs[""] = 0;103 104 for (const Record *Diag :105 Records.getAllDerivedDefinitions("Diagnostic")) {106 StringRef Category = getDiagnosticCategory(Diag, 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(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 138static bool beforeThanCompare(const Record *LHS, const Record *RHS) {139 assert(!LHS->getLoc().empty() && !RHS->getLoc().empty());140 return141 LHS->getLoc().front().getPointer() < RHS->getLoc().front().getPointer();142}143 144static bool diagGroupBeforeByName(const Record *LHS, const Record *RHS) {145 return LHS->getValueAsString("GroupName") <146 RHS->getValueAsString("GroupName");147}148 149using DiagsInGroupTy = std::map<StringRef, GroupInfo>;150 151/// Invert the 1-[0/1] mapping of diags to group into a one to many152/// mapping of groups to diags in the group.153static 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>(R->getValueInit("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("GroupName");163 DiagsInGroup[GroupName].DiagsInGroup.push_back(R);164 }165 166 // Add all DiagGroup's to the DiagsInGroup list to make sure we pick up empty167 // groups (these are warnings that GCC supports that clang never produces).168 for (const Record *Group : DiagGroups) {169 GroupInfo &GI = DiagsInGroup[Group->getValueAsString("GroupName")];170 GI.GroupName = Group->getName();171 GI.Defs.push_back(Group);172 173 for (const Record *SubGroup : Group->getValueAsListOfDefs("SubGroups"))174 GI.SubGroups.push_back(SubGroup->getValueAsString("GroupName"));175 }176 177 // Assign unique ID numbers to the groups.178 for (auto [IdNo, Iter] : enumerate(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 those191 // separately below.192 bool IsImplicit = false;193 for (const Record *Diag : Group.second.DiagsInGroup) {194 if (cast<DefInit>(Diag->getValueInit("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, SourceMgr::DK_Error,205 Twine("group '") + Group.first +206 "' is defined more than once");207 First = false;208 } else {209 SrcMgr.PrintMessage(Loc, SourceMgr::DK_Note, "also defined here");210 }211 }212 213 for (const Record *Diag : Group.second.DiagsInGroup) {214 if (!cast<DefInit>(Diag->getValueInit("Group"))->getDef()->isAnonymous())215 continue;216 217 SMLoc Loc = Diag->getLoc().front();218 if (First) {219 SrcMgr.PrintMessage(Loc, SourceMgr::DK_Error,220 Twine("group '") + Group.first +221 "' is implicitly defined more than once");222 First = false;223 } else {224 SrcMgr.PrintMessage(Loc, SourceMgr::DK_Note,225 "also implicitly defined here");226 }227 }228 }229}230 231//===----------------------------------------------------------------------===//232// Infer members of -Wpedantic.233//===----------------------------------------------------------------------===//234 235typedef std::vector<const Record *> RecordVec;236typedef DenseSet<const Record *> RecordSet;237typedef PointerUnion<RecordVec *, RecordSet *> VecOrSet;238 239namespace {240class 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;250public: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 immediately259 /// in -Wpedantic.260 void compute(VecOrSet DiagsInPedantic,261 VecOrSet GroupsInPedantic);262 263private: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 marked274 /// 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 namespace281 282bool InferPedantic::isSubGroupOfGroup(const Record *Group, StringRef GName) {283 StringRef GroupName = Group->getValueAsString("GroupName");284 if (GName == GroupName)285 return true;286 287 for (const Record *Parent : DiagGroupParents.getParents(Group))288 if (isSubGroupOfGroup(Parent, GName))289 return true;290 291 return false;292}293 294/// Determine if the diagnostic is an extension.295bool InferPedantic::isExtension(const Record *Diag) {296 return Diag->getValueAsDef("Class")->getName() == "CLASS_EXTENSION";297}298 299bool InferPedantic::isOffByDefault(const Record *Diag) {300 return Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name") ==301 "Ignored";302}303 304bool 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("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 diagnostic316 // or subgroup AND all of those diagnostics and subgroups are covered317 // by -Wpedantic via our computation.318 return V.first != 0 && V.first == *V.second;319}320 321void InferPedantic::markGroup(const Record *Group) {322 // If all the diagnostics and subgroups have been marked as being323 // covered by -Wpedantic, increment the count of parent groups. Once the324 // group's count is equal to the number of subgroups and diagnostics in325 // 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(Parent);329}330 331void InferPedantic::compute(VecOrSet DiagsInPedantic,332 VecOrSet GroupsInPedantic) {333 // All extensions that are not on by default are implicitly in the334 // "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(R) || !isOffByDefault(R))338 continue;339 DiagsSet.insert(R);340 if (const auto *Group = dyn_cast<DefInit>(R->getValueInit("Group"))) {341 const Record *GroupRec = Group->getDef();342 if (!isSubGroupOfGroup(GroupRec, "pedantic")) {343 markGroup(GroupRec);344 }345 }346 }347 348 // Compute the set of diagnostics that are directly in -Wpedantic. We349 // march through Diags a second time to ensure the results are emitted350 // in deterministic order.351 for (const Record *R : Diags) {352 if (!DiagsSet.count(R))353 continue;354 // Check if the group is implicitly in -Wpedantic. If so,355 // the diagnostic should not be directly included in the -Wpedantic356 // diagnostic group.357 if (const auto *Group = dyn_cast<DefInit>(R->getValueInit("Group")))358 if (groupInPedantic(Group->getDef()))359 continue;360 361 // The diagnostic is not included in a group that is (transitively) in362 // -Wpedantic. Include it in -Wpedantic directly.363 if (auto *V = dyn_cast<RecordVec *>(DiagsInPedantic))364 V->push_back(R);365 else366 cast<RecordSet *>(DiagsInPedantic)->insert(R);367 }368 369 if (!GroupsInPedantic)370 return;371 372 // Compute the set of groups that are directly in -Wpedantic. We373 // march through the groups to ensure the results are emitted374 /// 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(Parents, [&](const Record *R) { return groupInPedantic(R); });383 // If all the parents are in -Wpedantic, this means that this diagnostic384 // group will be indirectly included by -Wpedantic already. In that385 // case, do not add it directly to -Wpedantic. If the group has no386 // parents, obviously it should go into -Wpedantic.387 if (Parents.size() > 0 && AllParentsInPedantic)388 continue;389 390 if (auto *V = dyn_cast<RecordVec *>(GroupsInPedantic))391 V->push_back(Group);392 else393 cast<RecordSet *>(GroupsInPedantic)->insert(Group);394 }395}396 397namespace {398enum PieceKind {399 MultiPieceClass,400 TextPieceClass,401 PlaceholderPieceClass,402 SelectPieceClass,403 EnumSelectPieceClass,404 PluralPieceClass,405 DiffPieceClass,406 SubstitutionPieceClass,407};408 409enum 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 426static 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 case457 llvm_unreachable("invalid modifier type");458}459 460struct 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 470private:471 PieceKind ClassKind;472};473 474struct 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 486struct 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 497struct 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 508struct SelectPiece : Piece {509protected:510 SelectPiece(PieceKind Kind, ModifierType ModKind)511 : Piece(Kind), ModKind(ModKind) {}512 513public: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 527struct 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 538struct EnumValuePiece : Piece {539 ModifierType Kind;540};541 542struct 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 553struct 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 564struct 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 578struct 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("TextSubstitution")) {585 EvaluatingRecordGuard Guard(&EvaluatingRecord, S);586 Substitutions.try_emplace(587 S->getName(), DiagText(*this, S->getValueAsString("Substitution")));588 }589 590 // Check that no diagnostic definitions have the same name as a591 // substitution.592 for (const Record *Diag : Records.getAllDerivedDefinitions("Diagnostic")) {593 StringRef Name = Diag->getName();594 if (Substitutions.count(Name))595 llvm::PrintFatalError(596 Diag->getLoc(),597 "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(S->Name);611 if (It == Substitutions.end())612 llvm::PrintFatalError("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(EvaluatingRecord->getLoc(), Msg);620 }621 622private: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) noexcept652 : 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 further657 // motivation.658 DiagText &operator=(DiagText &&) = delete;659 660 // The copy constrcutor and copy assignment operator is defined as deleted661 // 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 671private: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 686template <class Derived> struct DiagTextVisitor {687 using ModifierMappingsType = std::optional<std::vector<int>>;688 689private:690 Derived &getDerived() { return static_cast<Derived &>(*this); }691 692public: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 724public: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 CASE741 }742 }743 744 void VisitSubstitution(SubstitutionPiece *P) {745 SubstitutionContext Guard(*this, P);746 Visit(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("Modifier value '" + std::to_string(Idx) +755 "' is not valid for this mapping (has " +756 std::to_string(ModifierMappings->size()) +757 " mappings)");758 return (*ModifierMappings)[Idx];759 }760 761 int mapIndex(int Idx) const {762 return mapIndex(Idx, ModifierMappings);763 }764 765protected:766 DiagnosticTextBuilder &Builder;767 ModifierMappingsType ModifierMappings;768};769 770void escapeRST(StringRef Str, std::string &Out) {771 for (auto K : Str) {772 if (StringRef("`*|_[]\\").count(K))773 Out.push_back('\\');774 Out.push_back(K);775 }776}777 778template <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 786template <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 794void makeRowSeparator(std::string &Str) {795 for (char &K : Str)796 K = (K == '|' ? '+' : '-');797}798 799struct 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>(OrigP)) {809 ModifierMappingsType NewMappings =810 getSubstitutionMappings(Sub, CurrentMappings);811 return gatherNodes(Builder.getSubstitution(Sub), NewMappings, Pieces);812 }813 if (auto *MD = dyn_cast<MultiPiece>(OrigP)) {814 for (Piece *Node : MD->Pieces)815 gatherNodes(Node, CurrentMappings, Pieces);816 return;817 }818 Pieces.push_back(std::make_pair(OrigP, CurrentMappings));819 }820 821 void VisitMulti(MultiPiece *P) {822 if (P->Pieces.empty()) {823 RST.push_back("");824 return;825 }826 827 if (P->Pieces.size() == 1)828 return Visit(P->Pieces[0]);829 830 // Flatten the list of nodes, replacing any substitution pieces with the831 // recursively flattened substituted node.832 std::vector<std::pair<Piece *, ModifierMappingsType>> Pieces;833 gatherNodes(P, 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(NodePair.first);843 844 if (Lines.empty())845 continue;846 847 // We need a vertical separator if either this or the previous piece is a848 // 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(Start + Lines.size(), EmptyLinePrefix);854 855 padToSameLength(Lines.begin(), 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(EmptyLinePrefix);868 RST.insert(RST.begin() + Start, EmptyLinePrefix);869 RST.insert(RST.end(), EmptyLinePrefix);870 }871 872 void VisitText(TextPiece *P) {873 RST.push_back("");874 auto &S = RST.back();875 876 StringRef T = P->Text;877 while (T.consume_front(" "))878 RST.back() += " |nbsp| ";879 880 std::string Suffix;881 while (T.consume_back(" "))882 Suffix += " |nbsp| ";883 884 if (!T.empty()) {885 S += ':';886 S += P->Role;887 S += ":`";888 escapeRST(T, S);889 S += '`';890 }891 892 S += Suffix;893 }894 895 void VisitPlaceholder(PlaceholderPiece *P) {896 RST.push_back(std::string(":placeholder:`") +897 char('A' + mapIndex(P->Index)) + "`");898 }899 900 void VisitSelect(SelectPiece *P) {901 std::vector<size_t> SeparatorIndexes;902 SeparatorIndexes.push_back(RST.size());903 RST.emplace_back();904 for (auto *O : P->Options) {905 Visit(O);906 SeparatorIndexes.push_back(RST.size());907 RST.emplace_back();908 }909 910 makeTableRows(RST.begin() + SeparatorIndexes.front(),911 RST.begin() + SeparatorIndexes.back() + 1);912 for (size_t I : SeparatorIndexes)913 makeRowSeparator(RST[I]);914 }915 916 void VisitEnumSelect(EnumSelectPiece *P) {917 // Document this as if it were a 'select', which properly prints all of the918 // options correctly in a readable/reasonable manner. There isn't really919 // 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(P->Parts[0]);932 FirstOption.Pieces.push_back(&E);933 FirstOption.Pieces.push_back(P->Parts[1]);934 FirstOption.Pieces.push_back(&F);935 FirstOption.Pieces.push_back(P->Parts[2]);936 937 SelectPiece Select(MT_Diff);938 Select.Options.push_back(&FirstOption);939 Select.Options.push_back(P->Parts[3]);940 941 VisitSelect(&Select);942 }943 944 std::vector<std::string> &RST;945};946 947struct DiagEnumPrinter : DiagTextVisitor<DiagEnumPrinter> {948public: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(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(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(P->OptionEnumNames))978 if (!Tup.value().empty())979 List.emplace_back(Tup.index(), Tup.value());980 981 Result.emplace_back(P->EnumName, List);982 }983 984 VisitSelect(P);985 }986};987 988struct DiagTextPrinter : DiagTextVisitor<DiagTextPrinter> {989public: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(Child);997 }998 void VisitText(TextPiece *P) { Result += P->Text; }999 void VisitPlaceholder(PlaceholderPiece *P) {1000 Result += "%";1001 Result += getModifierName(P->Kind);1002 addInt(mapIndex(P->Index));1003 }1004 void VisitSelect(SelectPiece *P) {1005 Result += "%";1006 Result += getModifierName(P->ModKind);1007 if (P->ModKind == MT_Select || P->ModKind == MT_EnumSelect) {1008 Result += "{";1009 for (auto *D : P->Options) {1010 Visit(D);1011 Result += '|';1012 }1013 if (!P->Options.empty())1014 Result.erase(--Result.end());1015 Result += '}';1016 }1017 addInt(mapIndex(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(P->OptionPrefixes, P->Options)) {1025 if (Prefix)1026 Visit(Prefix);1027 Visit(Option);1028 Result += "|";1029 }1030 if (!P->Options.empty())1031 Result.erase(--Result.end());1032 Result += '}';1033 addInt(mapIndex(P->Index));1034 }1035 1036 void VisitEnumSelect(EnumSelectPiece *P) {1037 // Print as if we are a 'select', which will result in the compiler just1038 // treating this like a normal select. This way we don't have to do any1039 // special work for the compiler to consume these.1040 VisitSelect(P);1041 }1042 1043 void VisitDiff(DiffPiece *P) {1044 Result += "%diff{";1045 Visit(P->Parts[0]);1046 Result += "$";1047 Visit(P->Parts[1]);1048 Result += "$";1049 Visit(P->Parts[2]);1050 Result += "|";1051 Visit(P->Parts[3]);1052 Result += "}";1053 addInt(mapIndex(P->Indexes[0]));1054 Result += ",";1055 addInt(mapIndex(P->Indexes[1]));1056 }1057 1058 void addInt(int Val) { Result += std::to_string(Val); }1059 1060 std::string &Result;1061};1062 1063int DiagnosticTextBuilder::DiagText::parseModifier(StringRef &Text) const {1064 if (Text.empty() || !isdigit(Text[0]))1065 Builder.PrintFatalError("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 1075Piece *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 do1085 End = Text.find_first_of(StopSet, 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(New<TextPiece>(Text.slice(0, End), "diagtext"));1092 Text = Text.substr(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("0123456789<{");1105 StringRef Modifier = Text.slice(0, ModLength);1106 Text = Text.substr(ModLength);1107 ModifierType ModType = StringSwitch<ModifierType>{Modifier}1108 .Case("select", MT_Select)1109 .Case("enum_select", MT_EnumSelect)1110 .Case("sub", MT_Sub)1111 .Case("diff", MT_Diff)1112 .Case("plural", MT_Plural)1113 .Case("s", MT_S)1114 .Case("ordinal", MT_Ordinal)1115 .Case("human", MT_Human)1116 .Case("q", MT_Q)1117 .Case("objcclass", MT_ObjCClass)1118 .Case("objcinstance", MT_ObjCInstance)1119 .Case("quoted", MT_Quoted)1120 .Case("", MT_Placeholder)1121 .Default(MT_Unknown);1122 1123 auto ExpectAndConsume = [&](StringRef Prefix) {1124 if (!Text.consume_front(Prefix))1125 Builder.PrintFatalError("expected '" + Prefix + "' while parsing %" +1126 Modifier);1127 };1128 1129 if (ModType != MT_EnumSelect && Text[0] == '<')1130 Builder.PrintFatalError("modifier '<' syntax not valid with %" +1131 Modifier);1132 1133 switch (ModType) {1134 case MT_Unknown:1135 Builder.PrintFatalError("Unknown modifier type: " + Modifier);1136 case MT_Select: {1137 SelectPiece *Select = New<SelectPiece>(MT_Select);1138 do {1139 Text = Text.drop_front(); // '{' or '|'1140 Select->Options.push_back(1141 parseDiagText(Text, StopAt::PipeOrCloseBrace));1142 assert(!Text.empty() && "malformed %select");1143 } while (Text.front() == '|');1144 ExpectAndConsume("}");1145 Select->Index = parseModifier(Text);1146 Parsed.push_back(Select);1147 continue;1148 }1149 case MT_EnumSelect: {1150 EnumSelectPiece *EnumSelect = New<EnumSelectPiece>();1151 if (Text[0] != '<')1152 Builder.PrintFatalError("expected '<' after " + Modifier);1153 1154 Text = Text.drop_front(); // Drop '<'1155 size_t EnumNameLen = Text.find_first_of('>');1156 EnumSelect->EnumName = Text.slice(0, EnumNameLen);1157 Text = Text.substr(EnumNameLen);1158 ExpectAndConsume(">");1159 1160 if (Text[0] != '{')1161 Builder.PrintFatalError("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("{");1171 EnumSelect->OptionEnumNames.push_back(Text.slice(0, OptionNameLen));1172 Text = Text.substr(OptionNameLen);1173 } else {1174 EnumSelect->OptionEnumNames.push_back({});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 parseDiagText(Text, 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(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(':');1203 if (End == StringRef::npos)1204 Builder.PrintFatalError("expected ':' while parsing %plural");1205 ++End;1206 assert(!Text.empty());1207 Plural->OptionPrefixes.push_back(1208 New<TextPiece>(Text.slice(0, End), "diagtext"));1209 Text = Text.substr(End);1210 Plural->Options.push_back(1211 parseDiagText(Text, StopAt::PipeOrCloseBrace));1212 assert(!Text.empty() && "malformed %plural");1213 } while (Text.front() == '|');1214 ExpectAndConsume("}");1215 Plural->Index = parseModifier(Text);1216 Parsed.push_back(Plural);1217 continue;1218 }1219 case MT_Sub: {1220 SubstitutionPiece *Sub = New<SubstitutionPiece>();1221 ExpectAndConsume("{");1222 size_t NameSize = Text.find_first_of('}');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(0, NameSize).str();1226 Text = Text.drop_front(NameSize);1227 ExpectAndConsume("}");1228 if (!Text.empty()) {1229 while (true) {1230 if (!isdigit(Text[0]))1231 break;1232 Sub->Modifiers.push_back(parseModifier(Text));1233 if (!Text.consume_front(","))1234 break;1235 assert(!Text.empty() && isdigit(Text[0]) &&1236 "expected another modifier");1237 }1238 }1239 Parsed.push_back(Sub);1240 continue;1241 }1242 case MT_Diff: {1243 DiffPiece *Diff = New<DiffPiece>();1244 ExpectAndConsume("{");1245 Diff->Parts[0] = parseDiagText(Text, StopAt::Dollar);1246 ExpectAndConsume("$");1247 Diff->Parts[1] = parseDiagText(Text, StopAt::Dollar);1248 ExpectAndConsume("$");1249 Diff->Parts[2] = parseDiagText(Text, StopAt::PipeOrCloseBrace);1250 ExpectAndConsume("|");1251 Diff->Parts[3] = parseDiagText(Text, StopAt::PipeOrCloseBrace);1252 ExpectAndConsume("}");1253 Diff->Indexes[0] = parseModifier(Text);1254 ExpectAndConsume(",");1255 Diff->Indexes[1] = parseModifier(Text);1256 Parsed.push_back(Diff);1257 continue;1258 }1259 case MT_S: {1260 SelectPiece *Select = New<SelectPiece>(ModType);1261 Select->Options.push_back(New<TextPiece>(""));1262 Select->Options.push_back(New<TextPiece>("s", "diagtext"));1263 Select->Index = parseModifier(Text);1264 Parsed.push_back(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(New<PlaceholderPiece>(ModType, parseModifier(Text)));1275 continue;1276 }1277 }1278 }1279 1280 return New<MultiPiece>(Parsed);1281}1282 1283std::vector<std::string>1284DiagnosticTextBuilder::buildForDocumentation(StringRef Severity,1285 const Record *R) {1286 EvaluatingRecordGuard Guard(&EvaluatingRecord, R);1287 StringRef Text = R->getValueAsString("Summary");1288 1289 DiagText D(*this, Text);1290 TextPiece *Prefix = D.New<TextPiece>(Severity, Severity);1291 Prefix->Text += ": ";1292 auto *MP = dyn_cast<MultiPiece>(D.Root);1293 if (!MP) {1294 MP = D.New<MultiPiece>();1295 MP->Pieces.push_back(D.Root);1296 D.Root = MP;1297 }1298 MP->Pieces.insert(MP->Pieces.begin(), Prefix);1299 std::vector<std::string> Result;1300 DiagTextDocPrinter{*this, Result}.Visit(D.Root);1301 return Result;1302}1303 1304DiagEnumPrinter::ResultTy DiagnosticTextBuilder::buildForEnum(const Record *R) {1305 EvaluatingRecordGuard Guard(&EvaluatingRecord, R);1306 StringRef Text = R->getValueAsString("Summary");1307 DiagText D(*this, Text);1308 DiagEnumPrinter::ResultTy Result;1309 DiagEnumPrinter{*this, Result}.Visit(D.Root);1310 return Result;1311}1312 1313std::string DiagnosticTextBuilder::buildForDefinition(const Record *R) {1314 EvaluatingRecordGuard Guard(&EvaluatingRecord, R);1315 StringRef Text = R->getValueAsString("Summary");1316 DiagText D(*this, Text);1317 std::string Result;1318 DiagTextPrinter{*this, Result}.Visit(D.Root);1319 return Result;1320}1321 1322} // namespace1323 1324//===----------------------------------------------------------------------===//1325// Warning Tables (.inc file) generation.1326//===----------------------------------------------------------------------===//1327 1328static bool isError(const Record &Diag) {1329 return Diag.getValueAsDef("Class")->getName() == "CLASS_ERROR";1330}1331 1332static bool isRemark(const Record &Diag) {1333 return Diag.getValueAsDef("Class")->getName() == "CLASS_REMARK";1334}1335 1336// Presumes the text has been split at the first whitespace or hyphen.1337static bool isExemptAtStart(StringRef Text) {1338 // Fast path, the first character is lowercase or not alphanumeric.1339 if (Text.empty() || isLower(Text[0]) || !isAlnum(Text[0]))1340 return true;1341 1342 // If the text is all uppercase (or numbers, +, or _), then we assume it's an1343 // acronym and that's allowed. This covers cases like ISO, C23, C++14, and1344 // OBJECT_MODE. However, if there's only a single letter other than "C", we1345 // do not exempt it so that we catch a case like "A really bad idea" while1346 // still allowing a case like "C does not allow...".1347 if (all_of(Text, [](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("AddressSanitizer", true)1355 .Case("CFString", true)1356 .Case("Clang", true)1357 .Case("Fuchsia", true)1358 .Case("GNUstep", true)1359 .Case("IBOutletCollection", true)1360 .Case("Microsoft", true)1361 .Case("Neon", true)1362 .StartsWith("NSInvocation", true) // NSInvocation, NSInvocation's1363 .Case("Objective", true) // Objective-C (hyphen is a word boundary)1364 .Case("OpenACC", true)1365 .Case("OpenCL", true)1366 .Case("OpenMP", true)1367 .Case("Pascal", true)1368 .Case("Swift", true)1369 .Case("Unicode", true)1370 .Case("Vulkan", true)1371 .Case("WebAssembly", true)1372 .Default(false);1373}1374 1375// Does not presume the text has been split at all.1376static bool isExemptAtEnd(StringRef Text) {1377 // Rather than come up with a list of characters that are allowed, we go the1378 // 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 1391static void verifyDiagnosticWording(const Record &Diag) {1392 StringRef FullDiagText = Diag.getValueAsString("Summary");1393 1394 auto DiagnoseStart = [&](StringRef Text) {1395 // Verify that the text does not start with a capital letter, except for1396 // special cases that are exempt like ISO and C++. Find the first word1397 // by looking for a word breaking character.1398 char Separators[] = {' ', '-', ',', '}'};1399 auto Iter = std::find_first_of(1400 Text.begin(), Text.end(), std::begin(Separators), std::end(Separators));1401 1402 StringRef First = Text.substr(0, Iter - Text.begin());1403 if (!isExemptAtStart(First)) {1404 PrintError(&Diag,1405 "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(&Diag, "Diagnostics should not end with punctuation; '" +1414 Text.substr(Text.size() - 1, 1) + "' is invalid");1415 }1416 };1417 1418 // If the diagnostic starts with %select, look through it to see whether any1419 // of the options will cause a problem.1420 if (FullDiagText.starts_with("%select{")) {1421 // Do a balanced delimiter scan from the start of the text to find the1422 // 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(PercentSelectBraceLen, Iter - FullDiagText.begin() -1442 PercentSelectBraceLen);1443 SmallVector<StringRef, 4> SelectPieces;1444 SelectText.split(SelectPieces, '|');1445 1446 // Walk over all of the individual pieces of select text to see if any of1447 // them start with an invalid character. If any of the select pieces is1448 // empty, we need to look at the first word after the %select to see1449 // whether that is invalid or not. If all of the pieces are fine, then we1450 // 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 else1456 DiagnoseStart(Piece);1457 }1458 1459 if (CheckSecondWord) {1460 // There was an empty select piece, so we need to check the second1461 // word. This catches situations like '%select{|fine}0 Not okay'. Add1462 // two to account for the closing curly brace and the number after it.1463 StringRef AfterSelect =1464 FullDiagText.substr(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 first1469 // 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 }, scan1474 // backwards to see if this is for a %select{...}0. If it is, we need to look1475 // at each piece to see whether it ends in punctuation or not.1476 bool StillNeedToDiagEnd = true;1477 if (isDigit(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 it1495 // is 'select'.1496 constexpr size_t SelectLen = sizeof("select") - 1;1497 bool IsSelect =1498 (FullDiagText.substr(Iter - SelectLen - FullDiagText.begin(),1499 SelectLen) == "select");1500 if (IsSelect) {1501 // Gather the content between the {} for the select in question so we can1502 // split it into pieces.1503 StillNeedToDiagEnd = false; // No longer need to handle the end.1504 StringRef SelectText =1505 FullDiagText.substr(Iter - FullDiagText.begin() + /*{*/ 1,1506 FullDiagText.end() - Iter - /*pos before }0*/ 3);1507 SmallVector<StringRef, 4> SelectPieces;1508 SelectText.split(SelectPieces, '|');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 it1518 // now.1519 if (StillNeedToDiagEnd)1520 DiagnoseEnd(FullDiagText);1521 1522 // FIXME: This could also be improved by looking for instances of clang or1523 // gcc in the diagnostic and recommend Clang or GCC instead. However, this1524 // 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 to1530/// simplify emitting compatibility warnings.1531void clang::EmitClangDiagsCompatIDs(const llvm::RecordKeeper &Records,1532 llvm::raw_ostream &OS,1533 const std::string &Component) {1534 ArrayRef<const Record *> Ids =1535 Records.getAllDerivedDefinitions("CompatWarningId");1536 1537 StringRef PrevComponent = "";1538 for (auto [I, R] : enumerate(make_pointee_range(Ids))) {1539 StringRef DiagComponent = R.getValueAsString("Component");1540 if (!Component.empty() && Component != DiagComponent)1541 continue;1542 1543 StringRef CompatDiagName = R.getValueAsString("Name");1544 StringRef Diag = R.getValueAsString("Diag");1545 StringRef DiagPre = R.getValueAsString("DiagPre");1546 int64_t CXXStdVer = R.getValueAsInt("Std");1547 1548 // We don't want to create empty enums since some compilers (including1549 // Clang) warn about that, so these macros are used to avoid having to1550 // 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 same1559 // name, e.g. 'constexpr_body_invalid_stmt' exists for C++14/20/23. It would1560 // be nice if we could combine all of them into a single compatibility diag1561 // 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 for1572/// a Clang component.1573void clang::EmitClangDiagsInterface(llvm::raw_ostream &OS,1574 const std::string &Component) {1575 if (Component.empty())1576 PrintFatalError("'-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(R"c++(1583namespace clang {1584namespace diag {1585enum {1586#define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \1587 SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \1588 ENUM,1589#define %sSTART1590#include "clang/Basic/Diagnostic%sKinds.inc"1591#undef DIAG1592 NUM_BUILTIN_%s_DIAGNOSTICS1593};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_END1605#undef DIAG_ENUM_ITEM1606#undef DIAG_ENUM1607} // end namespace diag1608 1609namespace 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_ID1617#undef DIAG_COMPAT_IDS_BEGIN1618#undef DIAG_COMPAT_IDS_END1619} // end namespace diag_compat1620} // end namespace clang1621)c++",1622 Upper, Comp, Upper, Comp, Comp);1623}1624 1625/// ClangDiagsEnumsEmitter - The top-level class emits .def files containing1626/// declarations of Clang diagnostic enums for selects.1627void 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("Diagnostic");1632 1633 llvm::SmallVector<std::pair<const Record *, std::string>> EnumerationNames;1634 1635 for (const Record &R : make_pointee_range(Diags)) {1636 DiagEnumPrinter::ResultTy Enums = DiagTextBuilder.buildForEnum(&R);1637 1638 for (auto &Enumeration : Enums) {1639 bool ShouldPrint =1640 Component.empty() || Component == R.getValueAsString("Component");1641 1642 auto PreviousByName = llvm::find_if(EnumerationNames, [&](auto &Prev) {1643 return Prev.second == Enumeration.first;1644 });1645 1646 if (PreviousByName != EnumerationNames.end()) {1647 PrintError(&R,1648 "Duplicate enumeration name '" + Enumeration.first + "'");1649 PrintNote(PreviousByName->first->getLoc(),1650 "Previous diagnostic is here");1651 }1652 1653 EnumerationNames.emplace_back(&R, 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::is_contained(EnumeratorNames, Enumerator.second))1661 PrintError(&R,1662 "Duplicate enumerator name '" + Enumerator.second + "'");1663 EnumeratorNames.push_back(Enumerator.second);1664 1665 if (ShouldPrint)1666 OS << "DIAG_ENUM_ITEM(" << Enumerator.first << ", "1667 << Enumerator.second << ")\n";1668 }1669 if (ShouldPrint)1670 OS << "DIAG_ENUM_END()\n";1671 }1672 }1673}1674 1675/// ClangDiagsDefsEmitter - The top-level class emits .def files containing1676/// declarations of Clang diagnostics.1677void clang::EmitClangDiagsDefs(const RecordKeeper &Records, raw_ostream &OS,1678 const std::string &Component) {1679 // Write the #if guard1680 if (!Component.empty()) {1681 std::string ComponentName = StringRef(Component).upper();1682 OS << "#ifdef " << ComponentName << "START\n";1683 OS << "__" << ComponentName << "START = DIAG_START_" << ComponentName1684 << ",\n";1685 OS << "#undef " << ComponentName << "START\n";1686 OS << "#endif\n\n";1687 }1688 1689 DiagnosticTextBuilder DiagTextBuilder(Records);1690 1691 ArrayRef<const Record *> Diags =1692 Records.getAllDerivedDefinitions("Diagnostic");1693 1694 ArrayRef<const Record *> DiagGroups =1695 Records.getAllDerivedDefinitions("DiagGroup");1696 1697 DiagsInGroupTy DiagsInGroup;1698 groupDiagnostics(Diags, DiagGroups, DiagsInGroup);1699 1700 DiagCategoryIDMap CategoryIDs(Records);1701 DiagGroupParentMap DGParentMap(Records);1702 1703 // Compute the set of diagnostics that are in -Wpedantic.1704 RecordSet DiagsInPedantic;1705 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);1706 inferPedantic.compute(&DiagsInPedantic, (RecordVec*)nullptr);1707 1708 for (const Record &R : make_pointee_range(Diags)) {1709 // Check if this is an error that is accidentally in a warning1710 // group.1711 if (isError(R)) {1712 if (const auto *Group = dyn_cast<DefInit>(R.getValueInit("Group"))) {1713 const Record *GroupRec = Group->getDef();1714 StringRef GroupName = GroupRec->getValueAsString("GroupName");1715 PrintFatalError(R.getLoc(), "Error " + R.getName() +1716 " cannot be in a warning group [" + GroupName + "]");1717 }1718 }1719 1720 // Check that all remarks have an associated diagnostic group.1721 if (isRemark(R)) {1722 if (!isa<DefInit>(R.getValueInit("Group"))) {1723 PrintFatalError(R.getLoc(), "Error " + R.getName() +1724 " not in any diagnostic group");1725 }1726 }1727 1728 // Filter by component.1729 if (!Component.empty() && Component != R.getValueAsString("Component"))1730 continue;1731 1732 // Validate diagnostic wording for common issues.1733 verifyDiagnosticWording(R);1734 1735 OS << "DIAG(" << R.getName() << ", ";1736 OS << R.getValueAsDef("Class")->getName();1737 OS << ", (unsigned)diag::Severity::"1738 << R.getValueAsDef("DefaultSeverity")->getValueAsString("Name");1739 1740 // Description string.1741 OS << ", \"";1742 OS.write_escaped(DiagTextBuilder.buildForDefinition(&R)) << '"';1743 1744 // Warning group associated with the diagnostic. This is stored as an index1745 // into the alphabetically sorted warning group table.1746 if (const auto *DI = dyn_cast<DefInit>(R.getValueInit("Group"))) {1747 auto I = DiagsInGroup.find(DI->getDef()->getValueAsString("GroupName"));1748 assert(I != DiagsInGroup.end());1749 OS << ", " << I->second.IDNo;1750 } else if (DiagsInPedantic.count(&R)) {1751 auto I = DiagsInGroup.find("pedantic");1752 assert(I != DiagsInGroup.end() && "pedantic group not defined");1753 OS << ", " << I->second.IDNo;1754 } else {1755 OS << ", 0";1756 }1757 1758 // SFINAE response.1759 OS << ", " << R.getValueAsDef("SFINAE")->getName();1760 1761 // Default warning has no Werror bit.1762 if (R.getValueAsBit("WarningNoWerror"))1763 OS << ", true";1764 else1765 OS << ", false";1766 1767 if (R.getValueAsBit("ShowInSystemHeader"))1768 OS << ", true";1769 else1770 OS << ", false";1771 1772 if (R.getValueAsBit("ShowInSystemMacro"))1773 OS << ", true";1774 else1775 OS << ", false";1776 1777 if (R.getValueAsBit("Deferrable"))1778 OS << ", true";1779 else1780 OS << ", false";1781 1782 // Category number.1783 OS << ", " << CategoryIDs.getID(getDiagnosticCategory(&R, DGParentMap));1784 OS << ")\n";1785 }1786}1787 1788//===----------------------------------------------------------------------===//1789// Warning Group Tables generation1790//===----------------------------------------------------------------------===//1791 1792static std::string getDiagCategoryEnum(StringRef name) {1793 if (name.empty())1794 return "DiagCat_None";1795 SmallString<256> enumName = StringRef("DiagCat_");1796 for (char C : name)1797 enumName += isalnum(C) ? C : '_';1798 return std::string(enumName);1799}1800 1801/// Emit the array of diagnostic subgroups.1802///1803/// The array of diagnostic subgroups contains for each group a list of its1804/// subgroups. The individual lists are separated by '-1'. Groups with no1805/// subgroups are skipped.1806///1807/// \code1808/// static const int16_t DiagSubGroups[] = {1809/// /* Empty */ -1,1810/// /* DiagSubGroup0 */ 142, -1,1811/// /* DiagSubGroup13 */ 265, 322, 399, -11812/// }1813/// \endcode1814///1815static void emitDiagSubGroups(DiagsInGroupTy &DiagsInGroup,1816 RecordVec &GroupsInPedantic, raw_ostream &OS) {1817 OS << "static const int16_t DiagSubGroups[] = {\n"1818 << " /* Empty */ -1,\n";1819 for (auto const &[Name, Group] : DiagsInGroup) {1820 const bool IsPedantic = Name == "pedantic";1821 const std::vector<StringRef> &SubGroups = Group.SubGroups;1822 if (!SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty())) {1823 OS << " /* DiagSubGroup" << Group.IDNo << " */ ";1824 for (StringRef SubGroup : SubGroups) {1825 auto RI = DiagsInGroup.find(SubGroup);1826 assert(RI != DiagsInGroup.end() && "Referenced without existing?");1827 OS << RI->second.IDNo << ", ";1828 }1829 // Emit the groups implicitly in "pedantic".1830 if (IsPedantic) {1831 for (auto const &Group : GroupsInPedantic) {1832 StringRef GroupName = Group->getValueAsString("GroupName");1833 auto RI = DiagsInGroup.find(GroupName);1834 assert(RI != DiagsInGroup.end() && "Referenced without existing?");1835 OS << RI->second.IDNo << ", ";1836 }1837 }1838 1839 OS << "-1,\n";1840 }1841 }1842 OS << "};\n\n";1843}1844 1845/// Emit the list of diagnostic arrays.1846///1847/// This data structure is a large array that contains itself arrays of varying1848/// size. Each array represents a list of diagnostics. The different arrays are1849/// separated by the value '-1'.1850///1851/// \code1852/// static const int16_t DiagArrays[] = {1853/// /* Empty */ -1,1854/// /* DiagArray1 */ diag::warn_pragma_message,1855/// -1,1856/// /* DiagArray2 */ diag::warn_abs_too_small,1857/// diag::warn_unsigned_abs,1858/// diag::warn_wrong_absolute_value_type,1859/// -11860/// };1861/// \endcode1862///1863static void emitDiagArrays(DiagsInGroupTy &DiagsInGroup,1864 RecordVec &DiagsInPedantic, raw_ostream &OS) {1865 OS << "static const int16_t DiagArrays[] = {\n"1866 << " /* Empty */ -1,\n";1867 for (const auto &[Name, Group] : DiagsInGroup) {1868 const bool IsPedantic = Name == "pedantic";1869 1870 const std::vector<const Record *> &V = Group.DiagsInGroup;1871 if (!V.empty() || (IsPedantic && !DiagsInPedantic.empty())) {1872 OS << " /* DiagArray" << Group.IDNo << " */ ";1873 for (auto *Record : V)1874 OS << "diag::" << Record->getName() << ", ";1875 // Emit the diagnostics implicitly in "pedantic".1876 if (IsPedantic) {1877 for (auto const &Diag : DiagsInPedantic)1878 OS << "diag::" << Diag->getName() << ", ";1879 }1880 OS << "-1,\n";1881 }1882 }1883 OS << "};\n\n";1884}1885 1886/// Emit a list of group names.1887///1888/// This creates an `llvm::StringTable` of all the diagnostic group names.1889static void emitDiagGroupNames(const StringToOffsetTable &GroupNames,1890 raw_ostream &OS) {1891 GroupNames.EmitStringTableDef(OS, "DiagGroupNames");1892 OS << "\n";1893}1894 1895/// Emit diagnostic arrays and related data structures.1896///1897/// This creates the actual diagnostic array, an array of diagnostic subgroups1898/// and an array of subgroup names.1899///1900/// \code1901/// #ifdef GET_DIAG_ARRAYS1902/// static const int16_t DiagArrays[];1903/// static const int16_t DiagSubGroups[];1904/// static constexpr llvm::StringTable DiagGroupNames;1905/// #endif1906/// \endcode1907static void emitAllDiagArrays(DiagsInGroupTy &DiagsInGroup,1908 RecordVec &DiagsInPedantic,1909 RecordVec &GroupsInPedantic,1910 const StringToOffsetTable &GroupNames,1911 raw_ostream &OS) {1912 OS << "\n#ifdef GET_DIAG_ARRAYS\n";1913 emitDiagArrays(DiagsInGroup, DiagsInPedantic, OS);1914 emitDiagSubGroups(DiagsInGroup, GroupsInPedantic, OS);1915 emitDiagGroupNames(GroupNames, OS);1916 OS << "#endif // GET_DIAG_ARRAYS\n\n";1917}1918 1919/// Emit diagnostic table.1920///1921/// The table is sorted by the name of the diagnostic group. Each element1922/// consists of the name of the diagnostic group (given as offset in the1923/// group name table), a reference to a list of diagnostics (optional) and a1924/// reference to a set of subgroups (optional).1925///1926/// \code1927/// #ifdef GET_DIAG_TABLE1928/// {/* abi */ 159, /* DiagArray11 */ 19, /* Empty */ 0},1929/// {/* aggregate-return */ 180, /* Empty */ 0, /* Empty */ 0},1930/// {/* all */ 197, /* Empty */ 0, /* DiagSubGroup13 */ 3},1931/// {/* deprecated */ 1981,/* DiagArray1 */ 348, /* DiagSubGroup3 */ 9},1932/// #endif1933/// \endcode1934static void emitDiagTable(DiagsInGroupTy &DiagsInGroup,1935 RecordVec &DiagsInPedantic,1936 RecordVec &GroupsInPedantic,1937 const StringToOffsetTable &GroupNames,1938 raw_ostream &OS) {1939 unsigned MaxLen = 0;1940 1941 for (auto const &I: DiagsInGroup)1942 MaxLen = std::max(MaxLen, (unsigned)I.first.size());1943 1944 OS << "\n#ifdef DIAG_ENTRY\n";1945 unsigned SubGroupIndex = 1, DiagArrayIndex = 1;1946 for (auto const &[Name, GroupInfo] : DiagsInGroup) {1947 // Group option string.1948 OS << "DIAG_ENTRY(";1949 OS << GroupInfo.GroupName << " /* ";1950 1951 if (Name.find_first_not_of("abcdefghijklmnopqrstuvwxyz"1952 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"1953 "0123456789!@#$%^*-+=:?") != std::string::npos)1954 PrintFatalError("Invalid character in diagnostic group '" + Name + "'");1955 OS << Name << " */, ";1956 OS << *GroupNames.GetStringOffset(Name) << ", ";1957 1958 // Special handling for 'pedantic'.1959 const bool IsPedantic = Name == "pedantic";1960 1961 // Diagnostics in the group.1962 const std::vector<const Record *> &V = GroupInfo.DiagsInGroup;1963 const bool hasDiags =1964 !V.empty() || (IsPedantic && !DiagsInPedantic.empty());1965 if (hasDiags) {1966 OS << "/* DiagArray" << GroupInfo.IDNo << " */ " << DiagArrayIndex1967 << ", ";1968 if (IsPedantic)1969 DiagArrayIndex += DiagsInPedantic.size();1970 DiagArrayIndex += V.size() + 1;1971 } else {1972 OS << "0, ";1973 }1974 1975 // Subgroups.1976 const std::vector<StringRef> &SubGroups = GroupInfo.SubGroups;1977 const bool hasSubGroups =1978 !SubGroups.empty() || (IsPedantic && !GroupsInPedantic.empty());1979 if (hasSubGroups) {1980 OS << "/* DiagSubGroup" << GroupInfo.IDNo << " */ " << SubGroupIndex1981 << ", ";1982 if (IsPedantic)1983 SubGroupIndex += GroupsInPedantic.size();1984 SubGroupIndex += SubGroups.size() + 1;1985 } else {1986 OS << "0, ";1987 }1988 1989 std::string Documentation = GroupInfo.Defs.back()1990 ->getValue("Documentation")1991 ->getValue()1992 ->getAsUnquotedString();1993 1994 OS << "R\"(" << StringRef(Documentation).trim() << ")\"";1995 1996 OS << ")\n";1997 }1998 OS << "#endif // DIAG_ENTRY\n\n";1999}2000 2001/// Emit the table of diagnostic categories.2002///2003/// The table has the form of macro calls that have two parameters. The2004/// category's name as well as an enum that represents the category. The2005/// table can be used by defining the macro 'CATEGORY' and including this2006/// table right after.2007///2008/// \code2009/// #ifdef GET_CATEGORY_TABLE2010/// CATEGORY("Semantic Issue", DiagCat_Semantic_Issue)2011/// CATEGORY("Lambda Issue", DiagCat_Lambda_Issue)2012/// #endif2013/// \endcode2014static void emitCategoryTable(const RecordKeeper &Records, raw_ostream &OS) {2015 DiagCategoryIDMap CategoriesByID(Records);2016 OS << "\n#ifdef GET_CATEGORY_TABLE\n";2017 for (auto const &C : CategoriesByID)2018 OS << "CATEGORY(\"" << C << "\", " << getDiagCategoryEnum(C) << ")\n";2019 OS << "#endif // GET_CATEGORY_TABLE\n\n";2020}2021 2022void clang::EmitClangDiagGroups(const RecordKeeper &Records, raw_ostream &OS) {2023 // Compute a mapping from a DiagGroup to all of its parents.2024 DiagGroupParentMap DGParentMap(Records);2025 2026 ArrayRef<const Record *> Diags =2027 Records.getAllDerivedDefinitions("Diagnostic");2028 2029 ArrayRef<const Record *> DiagGroups =2030 Records.getAllDerivedDefinitions("DiagGroup");2031 2032 DiagsInGroupTy DiagsInGroup;2033 groupDiagnostics(Diags, DiagGroups, DiagsInGroup);2034 2035 // All extensions are implicitly in the "pedantic" group. Record the2036 // implicit set of groups in the "pedantic" group, and use this information2037 // later when emitting the group information for Pedantic.2038 RecordVec DiagsInPedantic;2039 RecordVec GroupsInPedantic;2040 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);2041 inferPedantic.compute(&DiagsInPedantic, &GroupsInPedantic);2042 2043 StringToOffsetTable GroupNames;2044 for (const auto &[Name, Group] : DiagsInGroup) {2045 GroupNames.GetOrAddStringOffset(Name);2046 }2047 2048 emitAllDiagArrays(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames,2049 OS);2050 emitDiagTable(DiagsInGroup, DiagsInPedantic, GroupsInPedantic, GroupNames,2051 OS);2052 emitCategoryTable(Records, OS);2053}2054 2055//===----------------------------------------------------------------------===//2056// Diagnostic name index generation2057//===----------------------------------------------------------------------===//2058 2059void clang::EmitClangDiagsIndexName(const RecordKeeper &Records,2060 raw_ostream &OS) {2061 std::vector<const Record *> Diags =2062 Records.getAllDerivedDefinitions("Diagnostic");2063 2064 sort(Diags, [](const Record *LHS, const Record *RHS) {2065 return LHS->getName() < RHS->getName();2066 });2067 2068 for (const Record *Elem : Diags)2069 OS << "DIAG_NAME_INDEX(" << Elem->getName() << ")\n";2070}2071 2072//===----------------------------------------------------------------------===//2073// Diagnostic documentation generation2074//===----------------------------------------------------------------------===//2075 2076namespace docs {2077namespace {2078 2079bool isRemarkGroup(const Record *DiagGroup,2080 const DiagsInGroupTy &DiagsInGroup) {2081 bool AnyRemarks = false, AnyNonRemarks = false;2082 2083 std::function<void(StringRef)> Visit = [&](StringRef GroupName) {2084 auto &GroupInfo = DiagsInGroup.find(GroupName)->second;2085 for (const Record *Diag : GroupInfo.DiagsInGroup)2086 (isRemark(*Diag) ? AnyRemarks : AnyNonRemarks) = true;2087 for (StringRef Name : GroupInfo.SubGroups)2088 Visit(Name);2089 };2090 Visit(DiagGroup->getValueAsString("GroupName"));2091 2092 if (AnyRemarks && AnyNonRemarks)2093 PrintFatalError(2094 DiagGroup->getLoc(),2095 "Diagnostic group contains both remark and non-remark diagnostics");2096 return AnyRemarks;2097}2098 2099std::string getDefaultSeverity(const Record *Diag) {2100 return std::string(2101 Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name"));2102}2103 2104std::set<std::string> getDefaultSeverities(const Record *DiagGroup,2105 const DiagsInGroupTy &DiagsInGroup) {2106 std::set<std::string> States;2107 2108 std::function<void(StringRef)> Visit = [&](StringRef GroupName) {2109 auto &GroupInfo = DiagsInGroup.find(GroupName)->second;2110 for (const Record *Diag : GroupInfo.DiagsInGroup)2111 States.insert(getDefaultSeverity(Diag));2112 for (const auto &Name : GroupInfo.SubGroups)2113 Visit(Name);2114 };2115 Visit(DiagGroup->getValueAsString("GroupName"));2116 return States;2117}2118 2119void writeHeader(StringRef Str, raw_ostream &OS, char Kind = '-') {2120 OS << Str << "\n" << std::string(Str.size(), Kind) << "\n";2121}2122 2123void writeDiagnosticText(DiagnosticTextBuilder &Builder, const Record *R,2124 StringRef Role, raw_ostream &OS) {2125 StringRef Text = R->getValueAsString("Summary");2126 if (Text == "%0")2127 OS << "The text of this diagnostic is not controlled by Clang.\n\n";2128 else {2129 std::vector<std::string> Out = Builder.buildForDocumentation(Role, R);2130 for (auto &Line : Out)2131 OS << Line << "\n";2132 OS << "\n";2133 }2134}2135 2136} // namespace2137} // namespace docs2138 2139void clang::EmitClangDiagDocs(const RecordKeeper &Records, raw_ostream &OS) {2140 using namespace docs;2141 2142 // Get the documentation introduction paragraph.2143 const Record *Documentation = Records.getDef("GlobalDocumentation");2144 if (!Documentation) {2145 PrintFatalError("The Documentation top-level definition is missing, "2146 "no documentation will be generated.");2147 return;2148 }2149 2150 OS << Documentation->getValueAsString("Intro") << "\n";2151 2152 DiagnosticTextBuilder Builder(Records);2153 2154 ArrayRef<const Record *> Diags =2155 Records.getAllDerivedDefinitions("Diagnostic");2156 2157 std::vector<const Record *> DiagGroups =2158 Records.getAllDerivedDefinitions("DiagGroup");2159 sort(DiagGroups, diagGroupBeforeByName);2160 2161 DiagGroupParentMap DGParentMap(Records);2162 2163 DiagsInGroupTy DiagsInGroup;2164 groupDiagnostics(Diags, DiagGroups, DiagsInGroup);2165 2166 // Compute the set of diagnostics that are in -Wpedantic.2167 {2168 RecordSet DiagsInPedanticSet;2169 RecordSet GroupsInPedanticSet;2170 InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);2171 inferPedantic.compute(&DiagsInPedanticSet, &GroupsInPedanticSet);2172 auto &PedDiags = DiagsInGroup["pedantic"];2173 // Put the diagnostics into a deterministic order.2174 RecordVec DiagsInPedantic(DiagsInPedanticSet.begin(),2175 DiagsInPedanticSet.end());2176 RecordVec GroupsInPedantic(GroupsInPedanticSet.begin(),2177 GroupsInPedanticSet.end());2178 sort(DiagsInPedantic, beforeThanCompare);2179 sort(GroupsInPedantic, beforeThanCompare);2180 PedDiags.DiagsInGroup.insert(PedDiags.DiagsInGroup.end(),2181 DiagsInPedantic.begin(),2182 DiagsInPedantic.end());2183 for (auto *Group : GroupsInPedantic)2184 PedDiags.SubGroups.push_back(Group->getValueAsString("GroupName"));2185 }2186 2187 // FIXME: Write diagnostic categories and link to diagnostic groups in each.2188 2189 // Write out the diagnostic groups.2190 for (const Record *G : DiagGroups) {2191 bool IsRemarkGroup = isRemarkGroup(G, DiagsInGroup);2192 auto &GroupInfo = DiagsInGroup[G->getValueAsString("GroupName")];2193 bool IsSynonym = GroupInfo.DiagsInGroup.empty() &&2194 GroupInfo.SubGroups.size() == 1;2195 2196 writeHeader(((IsRemarkGroup ? "-R" : "-W") +2197 G->getValueAsString("GroupName")).str(),2198 OS);2199 2200 if (!IsSynonym) {2201 // FIXME: Ideally, all the diagnostics in a group should have the same2202 // default state, but that is not currently the case.2203 auto DefaultSeverities = getDefaultSeverities(G, DiagsInGroup);2204 if (!DefaultSeverities.empty() && !DefaultSeverities.count("Ignored")) {2205 bool AnyNonErrors = DefaultSeverities.count("Warning") ||2206 DefaultSeverities.count("Remark");2207 if (!AnyNonErrors)2208 OS << "This diagnostic is an error by default, but the flag ``-Wno-"2209 << G->getValueAsString("GroupName") << "`` can be used to disable "2210 << "the error.\n\n";2211 else2212 OS << "This diagnostic is enabled by default.\n\n";2213 } else if (DefaultSeverities.size() > 1) {2214 OS << "Some of the diagnostics controlled by this flag are enabled "2215 << "by default.\n\n";2216 }2217 }2218 2219 if (!GroupInfo.SubGroups.empty()) {2220 if (IsSynonym)2221 OS << "Synonym for ";2222 else if (GroupInfo.DiagsInGroup.empty())2223 OS << "Controls ";2224 else2225 OS << "Also controls ";2226 2227 sort(GroupInfo.SubGroups);2228 ListSeparator LS;2229 for (StringRef Name : GroupInfo.SubGroups)2230 OS << LS << "`" << (IsRemarkGroup ? "-R" : "-W") << Name << "`_";2231 OS << ".\n\n";2232 }2233 2234 if (!GroupInfo.DiagsInGroup.empty()) {2235 OS << "**Diagnostic text:**\n\n";2236 for (const Record *D : GroupInfo.DiagsInGroup) {2237 auto Severity = getDefaultSeverity(D);2238 Severity[0] = tolower(Severity[0]);2239 if (Severity == "ignored")2240 Severity = IsRemarkGroup ? "remark" : "warning";2241 2242 writeDiagnosticText(Builder, D, Severity, OS);2243 }2244 }2245 2246 auto Doc = G->getValueAsString("Documentation");2247 if (!Doc.empty())2248 OS << Doc;2249 else if (GroupInfo.SubGroups.empty() && GroupInfo.DiagsInGroup.empty())2250 OS << "This diagnostic flag exists for GCC compatibility, and has no "2251 "effect in Clang.\n";2252 OS << "\n";2253 }2254}2255