brintos

brintos / llvm-project-archived public Read only

0
0
Text · 73.9 KiB · 17078e2 Raw
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