//=- ClangDiagnosticsEmitter.cpp - Generate Clang diagnostics tables -*- C++ -*- // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // These tablegen backends emit Clang diagnostics tables. // //===----------------------------------------------------------------------===// #include "TableGenBackends.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Casting.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/StringToOffsetTable.h" #include "llvm/TableGen/TableGenBackend.h" #include <algorithm> #include <cctype> #include <functional> #include <map> #include <optional> #include <set> usingnamespacellvm; //===----------------------------------------------------------------------===// // Diagnostic category computation code. //===----------------------------------------------------------------------===// namespace { class DiagGroupParentMap { … }; } // end anonymous namespace. static std::string getCategoryFromDiagGroup(const Record *Group, DiagGroupParentMap &DiagGroupParents) { … } /// getDiagnosticCategory - Return the category that the specified diagnostic /// lives in. static std::string getDiagnosticCategory(const Record *R, DiagGroupParentMap &DiagGroupParents) { … } namespace { class DiagCategoryIDMap { … }; struct GroupInfo { … }; } // end anonymous namespace. static bool beforeThanCompare(const Record *LHS, const Record *RHS) { … } static bool diagGroupBeforeByName(const Record *LHS, const Record *RHS) { … } /// Invert the 1-[0/1] mapping of diags to group into a one to many /// mapping of groups to diags in the group. static void groupDiagnostics(ArrayRef<const Record *> Diags, ArrayRef<const Record *> DiagGroups, std::map<std::string, GroupInfo> &DiagsInGroup) { … } //===----------------------------------------------------------------------===// // Infer members of -Wpedantic. //===----------------------------------------------------------------------===// RecordVec; RecordSet; VecOrSet; namespace { class InferPedantic { … }; } // end anonymous namespace bool InferPedantic::isSubGroupOfGroup(const Record *Group, StringRef GName) { … } /// Determine if the diagnostic is an extension. bool InferPedantic::isExtension(const Record *Diag) { … } bool InferPedantic::isOffByDefault(const Record *Diag) { … } bool InferPedantic::groupInPedantic(const Record *Group, bool increment) { … } void InferPedantic::markGroup(const Record *Group) { … } void InferPedantic::compute(VecOrSet DiagsInPedantic, VecOrSet GroupsInPedantic) { … } namespace { enum PieceKind { … }; enum ModifierType { … }; static StringRef getModifierName(ModifierType MT) { … } struct Piece { … }; struct MultiPiece : Piece { … }; struct TextPiece : Piece { … }; struct PlaceholderPiece : Piece { … }; struct SelectPiece : Piece { … }; struct PluralPiece : SelectPiece { … }; struct DiffPiece : Piece { … }; struct SubstitutionPiece : Piece { … }; /// Diagnostic text, parsed into pieces. struct DiagnosticTextBuilder { … }; template <class Derived> struct DiagTextVisitor { … }; void escapeRST(StringRef Str, std::string &Out) { … } template <typename It> void padToSameLength(It Begin, It End) { … } template <typename It> void makeTableRows(It Begin, It End) { … } void makeRowSeparator(std::string &Str) { … } struct DiagTextDocPrinter : DiagTextVisitor<DiagTextDocPrinter> { … }; struct DiagTextPrinter : DiagTextVisitor<DiagTextPrinter> { … }; int DiagnosticTextBuilder::DiagText::parseModifier(StringRef &Text) const { … } Piece *DiagnosticTextBuilder::DiagText::parseDiagText(StringRef &Text, StopAt Stop) { … } std::vector<std::string> DiagnosticTextBuilder::buildForDocumentation(StringRef Severity, const Record *R) { … } std::string DiagnosticTextBuilder::buildForDefinition(const Record *R) { … } } // namespace //===----------------------------------------------------------------------===// // Warning Tables (.inc file) generation. //===----------------------------------------------------------------------===// static bool isError(const Record &Diag) { … } static bool isRemark(const Record &Diag) { … } // Presumes the text has been split at the first whitespace or hyphen. static bool isExemptAtStart(StringRef Text) { … } // Does not presume the text has been split at all. static bool isExemptAtEnd(StringRef Text) { … } static void verifyDiagnosticWording(const Record &Diag) { … } /// ClangDiagsDefsEmitter - The top-level class emits .def files containing /// declarations of Clang diagnostics. void clang::EmitClangDiagsDefs(const RecordKeeper &Records, raw_ostream &OS, const std::string &Component) { … } //===----------------------------------------------------------------------===// // Warning Group Tables generation //===----------------------------------------------------------------------===// static std::string getDiagCategoryEnum(StringRef name) { … } /// Emit the array of diagnostic subgroups. /// /// The array of diagnostic subgroups contains for each group a list of its /// subgroups. The individual lists are separated by '-1'. Groups with no /// subgroups are skipped. /// /// \code /// static const int16_t DiagSubGroups[] = { /// /* Empty */ -1, /// /* DiagSubGroup0 */ 142, -1, /// /* DiagSubGroup13 */ 265, 322, 399, -1 /// } /// \endcode /// static void emitDiagSubGroups(std::map<std::string, GroupInfo> &DiagsInGroup, RecordVec &GroupsInPedantic, raw_ostream &OS) { … } /// Emit the list of diagnostic arrays. /// /// This data structure is a large array that contains itself arrays of varying /// size. Each array represents a list of diagnostics. The different arrays are /// separated by the value '-1'. /// /// \code /// static const int16_t DiagArrays[] = { /// /* Empty */ -1, /// /* DiagArray1 */ diag::warn_pragma_message, /// -1, /// /* DiagArray2 */ diag::warn_abs_too_small, /// diag::warn_unsigned_abs, /// diag::warn_wrong_absolute_value_type, /// -1 /// }; /// \endcode /// static void emitDiagArrays(std::map<std::string, GroupInfo> &DiagsInGroup, RecordVec &DiagsInPedantic, raw_ostream &OS) { … } /// Emit a list of group names. /// /// This creates a long string which by itself contains a list of pascal style /// strings, which consist of a length byte directly followed by the string. /// /// \code /// static const char DiagGroupNames[] = { /// \000\020#pragma-messages\t#warnings\020CFString-literal" /// }; /// \endcode static void emitDiagGroupNames(const StringToOffsetTable &GroupNames, raw_ostream &OS) { … } /// Emit diagnostic arrays and related data structures. /// /// This creates the actual diagnostic array, an array of diagnostic subgroups /// and an array of subgroup names. /// /// \code /// #ifdef GET_DIAG_ARRAYS /// static const int16_t DiagArrays[]; /// static const int16_t DiagSubGroups[]; /// static const char DiagGroupNames[]; /// #endif /// \endcode static void emitAllDiagArrays(std::map<std::string, GroupInfo> &DiagsInGroup, RecordVec &DiagsInPedantic, RecordVec &GroupsInPedantic, const StringToOffsetTable &GroupNames, raw_ostream &OS) { … } /// Emit diagnostic table. /// /// The table is sorted by the name of the diagnostic group. Each element /// consists of the name of the diagnostic group (given as offset in the /// group name table), a reference to a list of diagnostics (optional) and a /// reference to a set of subgroups (optional). /// /// \code /// #ifdef GET_DIAG_TABLE /// {/* abi */ 159, /* DiagArray11 */ 19, /* Empty */ 0}, /// {/* aggregate-return */ 180, /* Empty */ 0, /* Empty */ 0}, /// {/* all */ 197, /* Empty */ 0, /* DiagSubGroup13 */ 3}, /// {/* deprecated */ 1981,/* DiagArray1 */ 348, /* DiagSubGroup3 */ 9}, /// #endif /// \endcode static void emitDiagTable(std::map<std::string, GroupInfo> &DiagsInGroup, RecordVec &DiagsInPedantic, RecordVec &GroupsInPedantic, const StringToOffsetTable &GroupNames, raw_ostream &OS) { … } /// Emit the table of diagnostic categories. /// /// The table has the form of macro calls that have two parameters. The /// category's name as well as an enum that represents the category. The /// table can be used by defining the macro 'CATEGORY' and including this /// table right after. /// /// \code /// #ifdef GET_CATEGORY_TABLE /// CATEGORY("Semantic Issue", DiagCat_Semantic_Issue) /// CATEGORY("Lambda Issue", DiagCat_Lambda_Issue) /// #endif /// \endcode static void emitCategoryTable(const RecordKeeper &Records, raw_ostream &OS) { … } void clang::EmitClangDiagGroups(const RecordKeeper &Records, raw_ostream &OS) { … } //===----------------------------------------------------------------------===// // Diagnostic name index generation //===----------------------------------------------------------------------===// namespace { struct RecordIndexElement { … }; } // end anonymous namespace. void clang::EmitClangDiagsIndexName(const RecordKeeper &Records, raw_ostream &OS) { … } //===----------------------------------------------------------------------===// // Diagnostic documentation generation //===----------------------------------------------------------------------===// namespace docs { namespace { bool isRemarkGroup(const Record *DiagGroup, const std::map<std::string, GroupInfo> &DiagsInGroup) { … } std::string getDefaultSeverity(const Record *Diag) { … } std::set<std::string> getDefaultSeverities(const Record *DiagGroup, const std::map<std::string, GroupInfo> &DiagsInGroup) { … } void writeHeader(StringRef Str, raw_ostream &OS, char Kind = '-') { … } void writeDiagnosticText(DiagnosticTextBuilder &Builder, const Record *R, StringRef Role, raw_ostream &OS) { … } } // namespace } // namespace docs void clang::EmitClangDiagDocs(const RecordKeeper &Records, raw_ostream &OS) { … }