EricWF updated this revision to Diff 146498.
EricWF marked 2 inline comments as done.
EricWF added a comment.
Address @rsmiths comments.
- Prefer `int` to `unsigned`.
- Substitute `note_ovl_candidate`.
- Fix issues in docs.
https://reviews.llvm.org/D46740
Files:
docs/InternalsManual.rst
include/clang/Basic/Diagnostic.td
include/clang/Basic/DiagnosticSemaKinds.td
lib/Basic/Diagnostic.cpp
lib/Sema/SemaOverload.cpp
test/SemaCXX/anonymous-struct.cpp
test/SemaCXX/cxx98-compat.cpp
test/TableGen/DiagnosticBase.inc
test/TableGen/DiagnosticDocs.inc
test/TableGen/emit-diag-docs.td
test/TableGen/text-substitution.td
test/lit.cfg.py
utils/TableGen/ClangDiagnosticsEmitter.cpp
Index: utils/TableGen/ClangDiagnosticsEmitter.cpp
===================================================================
--- utils/TableGen/ClangDiagnosticsEmitter.cpp
+++ utils/TableGen/ClangDiagnosticsEmitter.cpp
@@ -14,12 +14,13 @@
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/Optional.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/STLExtras.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"
@@ -441,6 +442,722 @@
}
}
+namespace {
+enum PieceKind {
+ MultiPieceClass,
+ TextPieceClass,
+ PlaceholderPieceClass,
+ SelectPieceClass,
+ PluralPieceClass,
+ DiffPieceClass,
+ SubstitutionPieceClass,
+};
+
+enum ModifierType {
+ MT_Unknown,
+ MT_Placeholder,
+ MT_Select,
+ MT_Sub,
+ MT_Plural,
+ MT_Diff,
+ MT_Ordinal,
+ MT_S,
+ MT_Q,
+ MT_ObjCClass,
+ MT_ObjCInstance,
+};
+
+static StringRef getModifierName(ModifierType MT) {
+ switch (MT) {
+ case MT_Select:
+ return "select";
+ case MT_Sub:
+ return "sub";
+ case MT_Diff:
+ return "diff";
+ case MT_Plural:
+ return "plural";
+ case MT_Ordinal:
+ return "ordinal";
+ case MT_S:
+ return "s";
+ case MT_Q:
+ return "q";
+ case MT_Placeholder:
+ return "";
+ case MT_ObjCClass:
+ return "objcclass";
+ case MT_ObjCInstance:
+ return "objcinstance";
+ case MT_Unknown:
+ llvm_unreachable("invalid modifier type");
+ }
+}
+
+struct Piece {
+ // This type and its derived classes are move-only.
+ Piece(PieceKind Kind) : ClassKind(Kind) {}
+ Piece(Piece const &O) = delete;
+ Piece &operator=(Piece const &) = delete;
+ virtual ~Piece() {}
+
+ PieceKind getPieceClass() const { return ClassKind; }
+ static bool classof(const Piece *) { return true; }
+
+private:
+ PieceKind ClassKind;
+};
+
+struct MultiPiece : Piece {
+ MultiPiece() : Piece(MultiPieceClass) {}
+ MultiPiece(std::vector<Piece *> Pieces)
+ : Piece(MultiPieceClass), Pieces(std::move(Pieces)) {}
+
+ std::vector<Piece *> Pieces;
+
+ static bool classof(const Piece *P) {
+ return P->getPieceClass() == MultiPieceClass;
+ }
+};
+
+struct TextPiece : Piece {
+ StringRef Role;
+ std::string Text;
+ TextPiece(StringRef Text, StringRef Role = "")
+ : Piece(TextPieceClass), Role(Role), Text(Text.str()) {}
+
+ static bool classof(const Piece *P) {
+ return P->getPieceClass() == TextPieceClass;
+ }
+};
+
+struct PlaceholderPiece : Piece {
+ ModifierType Kind;
+ int Index;
+ PlaceholderPiece(ModifierType Kind, int Index)
+ : Piece(PlaceholderPieceClass), Kind(Kind), Index(Index) {}
+
+ static bool classof(const Piece *P) {
+ return P->getPieceClass() == PlaceholderPieceClass;
+ }
+};
+
+struct SelectPiece : Piece {
+protected:
+ SelectPiece(PieceKind Kind, ModifierType ModKind)
+ : Piece(Kind), ModKind(ModKind) {}
+
+public:
+ SelectPiece(ModifierType ModKind) : SelectPiece(SelectPieceClass, ModKind) {}
+
+ ModifierType ModKind;
+ std::vector<Piece *> Options;
+ int Index;
+
+ static bool classof(const Piece *P) {
+ return P->getPieceClass() == SelectPieceClass ||
+ P->getPieceClass() == PluralPieceClass;
+ }
+};
+
+struct PluralPiece : SelectPiece {
+ PluralPiece() : SelectPiece(PluralPieceClass, MT_Plural) {}
+
+ std::vector<Piece *> OptionPrefixes;
+ int Index;
+
+ static bool classof(const Piece *P) {
+ return P->getPieceClass() == PluralPieceClass;
+ }
+};
+
+struct DiffPiece : Piece {
+ DiffPiece() : Piece(DiffPieceClass) {}
+
+ Piece *Options[2] = {};
+ int Indexes[2] = {};
+
+ static bool classof(const Piece *P) {
+ return P->getPieceClass() == DiffPieceClass;
+ }
+};
+
+struct SubstitutionPiece : Piece {
+ SubstitutionPiece() : Piece(SubstitutionPieceClass) {}
+
+ std::string Name;
+ std::vector<int> Modifiers;
+
+ static bool classof(const Piece *P) {
+ return P->getPieceClass() == SubstitutionPieceClass;
+ }
+};
+
+/// Diagnostic text, parsed into pieces.
+
+
+struct DiagnosticTextBuilder {
+ DiagnosticTextBuilder(DiagnosticTextBuilder const &) = delete;
+ DiagnosticTextBuilder &operator=(DiagnosticTextBuilder const &) = delete;
+
+ DiagnosticTextBuilder(RecordKeeper &Records) {
+ // Build up the list of substitution records.
+ for (auto *S : Records.getAllDerivedDefinitions("TextSubstitution")) {
+ EvaluatingRecordGuard Guard(&EvaluatingRecord, S);
+ Substitutions.try_emplace(
+ S->getName(), DiagText(*this, S->getValueAsString("Substitution")));
+ }
+ }
+
+ std::vector<std::string> buildForDocumentation(StringRef Role,
+ const Record *R);
+ std::string buildForDefinition(const Record *R);
+
+ Piece *getSubstitution(SubstitutionPiece *S) const {
+ auto It = Substitutions.find(S->Name);
+ if (It == Substitutions.end())
+ PrintFatalError("Failed to find substitution with name: " + S->Name);
+ return It->second.Root;
+ }
+
+ void PrintFatalError(llvm::Twine const &Msg) const {
+ assert(EvaluatingRecord && "not evaluating a record?");
+ llvm::PrintFatalError(EvaluatingRecord->getLoc(), Msg);
+ }
+
+private:
+ struct DiagText {
+ DiagnosticTextBuilder &Builder;
+ std::vector<Piece *> AllocatedPieces;
+ Piece *Root = nullptr;
+
+ template <class T, class... Args> T *New(Args &&... args) {
+ static_assert(std::is_base_of<Piece, T>::value, "must be piece");
+ T *Mem = new T(std::forward<Args>(args)...);
+ AllocatedPieces.push_back(Mem);
+ return Mem;
+ }
+
+ DiagText(DiagnosticTextBuilder &Builder, StringRef Text)
+ : Builder(Builder), Root(parseDiagText(Text)) {}
+
+ Piece *parseDiagText(StringRef &Text, bool Nested = false);
+ int parseModifier(StringRef &) const;
+
+ public:
+ DiagText(DiagText &&O) noexcept
+ : Builder(O.Builder), AllocatedPieces(std::move(O.AllocatedPieces)),
+ Root(O.Root) {
+ O.Root = nullptr;
+ }
+
+ ~DiagText() {
+ for (Piece *P : AllocatedPieces)
+ delete P;
+ }
+ };
+
+private:
+ const Record *EvaluatingRecord = nullptr;
+ struct EvaluatingRecordGuard {
+ EvaluatingRecordGuard(const Record **Dest, const Record *New)
+ : Dest(Dest), Old(*Dest) {
+ *Dest = New;
+ }
+ ~EvaluatingRecordGuard() { *Dest = Old; }
+ const Record **Dest;
+ const Record *Old;
+ };
+
+ StringMap<DiagText> Substitutions;
+};
+
+template <class Derived> struct DiagTextVisitor {
+ using ModifierMappingsType = Optional<std::vector<int>>;
+
+private:
+ Derived &getDerived() { return static_cast<Derived &>(*this); }
+
+public:
+ std::vector<int>
+ getSubstitutionMappings(SubstitutionPiece *P,
+ const ModifierMappingsType &Mappings) const {
+ std::vector<int> NewMappings;
+ for (int Idx : P->Modifiers)
+ NewMappings.push_back(mapIndex(Idx, Mappings));
+ return NewMappings;
+ }
+
+ struct SubstitutionContext {
+ SubstitutionContext(DiagTextVisitor &Visitor, SubstitutionPiece *P)
+ : Visitor(Visitor) {
+ Substitution = Visitor.Builder.getSubstitution(P);
+ OldMappings = std::move(Visitor.ModifierMappings);
+ std::vector<int> NewMappings =
+ Visitor.getSubstitutionMappings(P, OldMappings);
+ Visitor.ModifierMappings = std::move(NewMappings);
+ }
+
+ ~SubstitutionContext() {
+ Visitor.ModifierMappings = std::move(OldMappings);
+ }
+
+ private:
+ DiagTextVisitor &Visitor;
+ Optional<std::vector<int>> OldMappings;
+
+ public:
+ Piece *Substitution;
+ };
+
+public:
+ DiagTextVisitor(DiagnosticTextBuilder &Builder) : Builder(Builder) {}
+
+ void Visit(Piece *P) {
+ switch (P->getPieceClass()) {
+#define CASE(T) \
+ case T##PieceClass: \
+ return getDerived().Visit##T(static_cast<T##Piece *>(P))
+ CASE(Multi);
+ CASE(Text);
+ CASE(Placeholder);
+ CASE(Select);
+ CASE(Plural);
+ CASE(Diff);
+ CASE(Substitution);
+#undef CASE
+ }
+ }
+
+ void VisitSubstitution(SubstitutionPiece *P) {
+ SubstitutionContext Guard(*this, P);
+ Visit(Guard.Substitution);
+ }
+
+ int mapIndex(int Idx,
+ ModifierMappingsType const &ModifierMappings) const {
+ if (!ModifierMappings)
+ return Idx;
+ if (ModifierMappings->size() <= Idx)
+ Builder.PrintFatalError("Modifier value '" + std::to_string(Idx) +
+ "' is not valid for this mapping (has " +
+ std::to_string(ModifierMappings->size()) +
+ " mappings)");
+ return (*ModifierMappings)[Idx];
+ }
+
+ int mapIndex(int Idx) const {
+ return mapIndex(Idx, ModifierMappings);
+ }
+
+protected:
+ DiagnosticTextBuilder &Builder;
+ ModifierMappingsType ModifierMappings;
+};
+
+void escapeRST(StringRef Str, std::string &Out) {
+ for (auto K : Str) {
+ if (StringRef("`*|_[]\\").count(K))
+ Out.push_back('\\');
+ Out.push_back(K);
+ }
+}
+
+template <typename It> void padToSameLength(It Begin, It End) {
+ size_t Width = 0;
+ for (It I = Begin; I != End; ++I)
+ Width = std::max(Width, I->size());
+ for (It I = Begin; I != End; ++I)
+ (*I) += std::string(Width - I->size(), ' ');
+}
+
+template <typename It> void makeTableRows(It Begin, It End) {
+ if (Begin == End)
+ return;
+ padToSameLength(Begin, End);
+ for (It I = Begin; I != End; ++I)
+ *I = "|" + *I + "|";
+}
+
+void makeRowSeparator(std::string &Str) {
+ for (char &K : Str)
+ K = (K == '|' ? '+' : '-');
+}
+
+struct DiagTextDocPrinter : DiagTextVisitor<DiagTextDocPrinter> {
+ using BaseTy = DiagTextVisitor<DiagTextDocPrinter>;
+ DiagTextDocPrinter(DiagnosticTextBuilder &Builder,
+ std::vector<std::string> &RST)
+ : BaseTy(Builder), RST(RST) {}
+
+ void gatherNodes(
+ Piece *OrigP, const ModifierMappingsType &CurrentMappings,
+ std::vector<std::pair<Piece *, ModifierMappingsType>> &Pieces) const {
+ if (auto *Sub = dyn_cast<SubstitutionPiece>(OrigP)) {
+ ModifierMappingsType NewMappings =
+ getSubstitutionMappings(Sub, CurrentMappings);
+ return gatherNodes(Builder.getSubstitution(Sub), NewMappings, Pieces);
+ }
+ if (auto *MD = dyn_cast<MultiPiece>(OrigP)) {
+ for (auto *Node : MD->Pieces)
+ gatherNodes(Node, CurrentMappings, Pieces);
+ return;
+ }
+ Pieces.push_back(std::make_pair(OrigP, CurrentMappings));
+ }
+
+ void VisitMulti(MultiPiece *P) {
+ if (P->Pieces.empty()) {
+ RST.push_back("");
+ return;
+ }
+
+ if (P->Pieces.size() == 1)
+ return Visit(P->Pieces[0]);
+
+ // Flatten the list of nodes, replacing any substitution pieces with the
+ // recursively flattened substituted node.
+ std::vector<std::pair<Piece *, ModifierMappingsType>> Pieces;
+ gatherNodes(P, ModifierMappings, Pieces);
+
+ std::string EmptyLinePrefix;
+ size_t Start = RST.size();
+ bool HasMultipleLines = true;
+ for (auto &NodePair : Pieces) {
+ std::vector<std::string> Lines;
+ DiagTextDocPrinter Visitor{Builder, Lines};
+ Visitor.ModifierMappings = NodePair.second;
+ Visitor.Visit(NodePair.first);
+
+ if (Lines.empty())
+ continue;
+
+ // We need a vertical separator if either this or the previous piece is a
+ // multi-line piece, or this is the last piece.
+ const char *Separator = (Lines.size() > 1 || HasMultipleLines) ? "|" : "";
+ HasMultipleLines = Lines.size() > 1;
+
+ if (Start + Lines.size() > RST.size())
+ RST.resize(Start + Lines.size(), EmptyLinePrefix);
+
+ padToSameLength(Lines.begin(), Lines.end());
+ for (size_t I = 0; I != Lines.size(); ++I)
+ RST[Start + I] += Separator + Lines[I];
+ std::string Empty(Lines[0].size(), ' ');
+ for (size_t I = Start + Lines.size(); I != RST.size(); ++I)
+ RST[I] += Separator + Empty;
+ EmptyLinePrefix += Separator + Empty;
+ }
+ for (size_t I = Start; I != RST.size(); ++I)
+ RST[I] += "|";
+ EmptyLinePrefix += "|";
+
+ makeRowSeparator(EmptyLinePrefix);
+ RST.insert(RST.begin() + Start, EmptyLinePrefix);
+ RST.insert(RST.end(), EmptyLinePrefix);
+ }
+
+ void VisitText(TextPiece *P) {
+ RST.push_back("");
+ auto &S = RST.back();
+
+ StringRef T = P->Text;
+ while (!T.empty() && T.front() == ' ') {
+ RST.back() += " |nbsp| ";
+ T = T.drop_front();
+ }
+
+ std::string Suffix;
+ while (!T.empty() && T.back() == ' ') {
+ Suffix += " |nbsp| ";
+ T = T.drop_back();
+ }
+
+ if (!T.empty()) {
+ S += ':';
+ S += P->Role;
+ S += ":`";
+ escapeRST(T, S);
+ S += '`';
+ }
+
+ S += Suffix;
+ }
+
+ void VisitPlaceholder(PlaceholderPiece *P) {
+ RST.push_back(std::string(":placeholder:`") +
+ char('A' + mapIndex(P->Index)) + "`");
+ }
+
+ void VisitSelect(SelectPiece *P) {
+ std::vector<size_t> SeparatorIndexes;
+ SeparatorIndexes.push_back(RST.size());
+ RST.emplace_back();
+ for (auto *O : P->Options) {
+ Visit(O);
+ SeparatorIndexes.push_back(RST.size());
+ RST.emplace_back();
+ }
+
+ makeTableRows(RST.begin() + SeparatorIndexes.front(),
+ RST.begin() + SeparatorIndexes.back() + 1);
+ for (size_t I : SeparatorIndexes)
+ makeRowSeparator(RST[I]);
+ }
+
+ void VisitPlural(PluralPiece *P) { VisitSelect(P); }
+
+ void VisitDiff(DiffPiece *P) { Visit(P->Options[1]); }
+
+ std::vector<std::string> &RST;
+};
+
+struct DiagTextPrinter : DiagTextVisitor<DiagTextPrinter> {
+public:
+ using BaseTy = DiagTextVisitor<DiagTextPrinter>;
+ DiagTextPrinter(DiagnosticTextBuilder &Builder, std::string &Result)
+ : BaseTy(Builder), Result(Result) {}
+
+ void VisitMulti(MultiPiece *P) {
+ for (auto *Child : P->Pieces)
+ Visit(Child);
+ }
+ void VisitText(TextPiece *P) { Result += P->Text; }
+ void VisitPlaceholder(PlaceholderPiece *P) {
+ Result += "%";
+ Result += getModifierName(P->Kind);
+ addInt(mapIndex(P->Index));
+ }
+ void VisitSelect(SelectPiece *P) {
+ Result += "%";
+ Result += getModifierName(P->ModKind);
+ if (P->ModKind == MT_Select) {
+ Result += "{";
+ for (auto *D : P->Options) {
+ Visit(D);
+ Result += '|';
+ }
+ if (!P->Options.empty())
+ Result.erase(--Result.end());
+ Result += '}';
+ }
+ addInt(mapIndex(P->Index));
+ }
+
+ void VisitPlural(PluralPiece *P) {
+ Result += "%plural{";
+ assert(P->Options.size() == P->OptionPrefixes.size());
+ for (unsigned I = 0, End = P->Options.size(); I < End; ++I) {
+ if (P->OptionPrefixes[I])
+ Visit(P->OptionPrefixes[I]);
+ Visit(P->Options[I]);
+ Result += "|";
+ }
+ if (!P->Options.empty())
+ Result.erase(--Result.end());
+ Result += '}';
+ addInt(mapIndex(P->Index));
+ }
+
+ void VisitDiff(DiffPiece *P) {
+ Result += "%diff{";
+ Visit(P->Options[0]);
+ Result += "|";
+ Visit(P->Options[1]);
+ Result += "}";
+ addInt(mapIndex(P->Indexes[0]));
+ Result += ",";
+ addInt(mapIndex(P->Indexes[1]));
+ }
+
+ void addInt(int Val) {
+ assert(Val < 10);
+ Result += static_cast<char>('0' + Val);
+ }
+
+ std::string &Result;
+};
+
+int DiagnosticTextBuilder::DiagText::parseModifier(StringRef &Text) const {
+ if (Text.empty() || !isdigit(Text[0]))
+ Builder.PrintFatalError("expected modifier in diagnostic");
+
+ int Val = Text[0] - '0';
+ Text = Text.drop_front();
+ return Val;
+}
+
+Piece *DiagnosticTextBuilder::DiagText::parseDiagText(StringRef &Text,
+ bool Nested) {
+ std::vector<Piece *> Parsed;
+
+ while (!Text.empty()) {
+ size_t End = (size_t)-2;
+ do
+ End = Nested ? Text.find_first_of("%|}", End + 2)
+ : Text.find_first_of('%', End + 2);
+ while (End < Text.size() - 1 && Text[End] == '%' &&
+ (Text[End + 1] == '%' || Text[End + 1] == '|'));
+
+ if (End) {
+ Parsed.push_back(New<TextPiece>(Text.slice(0, End), "diagtext"));
+ Text = Text.slice(End, StringRef::npos);
+ if (Text.empty())
+ break;
+ }
+
+ if (Text[0] == '|' || Text[0] == '}')
+ break;
+
+ // Drop the '%'.
+ Text = Text.drop_front();
+
+ // Extract the (optional) modifier.
+ size_t ModLength = Text.find_first_of("0123456789{");
+ StringRef Modifier = Text.slice(0, ModLength);
+ Text = Text.slice(ModLength, StringRef::npos);
+ ModifierType ModType = llvm::StringSwitch<ModifierType>{Modifier}
+ .Case("select", MT_Select)
+ .Case("sub", MT_Sub)
+ .Case("diff", MT_Diff)
+ .Case("plural", MT_Plural)
+ .Case("s", MT_S)
+ .Case("ordinal", MT_Ordinal)
+ .Case("q", MT_Q)
+ .Case("objcclass", MT_ObjCClass)
+ .Case("objcinstance", MT_ObjCInstance)
+ .Case("", MT_Placeholder)
+ .Default(MT_Unknown);
+
+ switch (ModType) {
+ case MT_Unknown:
+ Builder.PrintFatalError("Unknown modifier type: " + Modifier);
+ case MT_Select: {
+ SelectPiece *Select = New<SelectPiece>(MT_Select);
+ do {
+ Text = Text.drop_front(); // '{' or '|'
+ Select->Options.push_back(parseDiagText(Text, true));
+ assert(!Text.empty() && "malformed %select");
+ } while (Text.front() == '|');
+ // Drop the trailing '}'.
+ Text = Text.drop_front(1);
+ Select->Index = parseModifier(Text);
+ Parsed.push_back(Select);
+ continue;
+ }
+ case MT_Plural: {
+ PluralPiece *Plural = New<PluralPiece>();
+ do {
+ Text = Text.drop_front(); // '{' or '|'
+ size_t End = Text.find_first_of(":");
+ if (End == StringRef::npos)
+ Builder.PrintFatalError("expected ':' while parsing %plural");
+ ++End;
+ assert(!Text.empty());
+ Plural->OptionPrefixes.push_back(
+ New<TextPiece>(Text.slice(0, End), "diagtext"));
+ Text = Text.slice(End, StringRef::npos);
+ Plural->Options.push_back(parseDiagText(Text, true));
+ assert(!Text.empty() && "malformed %select");
+ } while (Text.front() == '|');
+ // Drop the trailing '}'.
+ Text = Text.drop_front(1);
+ Plural->Index = parseModifier(Text);
+ Parsed.push_back(Plural);
+ continue;
+ }
+ case MT_Sub: {
+ SubstitutionPiece *Sub = New<SubstitutionPiece>();
+ Text = Text.drop_front(); // '{'
+ size_t NameSize = Text.find_first_of('}');
+ assert(NameSize != size_t(-1) && "failed to find the end of the name");
+ assert(NameSize != 0 && "empty name?");
+ Sub->Name = Text.substr(0, NameSize).str();
+ Text = Text.drop_front(NameSize);
+ Text = Text.drop_front(); // '}'
+ if (!Text.empty()) {
+ while (true) {
+ if (!isdigit(Text[0]))
+ break;
+ Sub->Modifiers.push_back(parseModifier(Text));
+ if (Text.empty() || Text[0] != ',')
+ break;
+ Text = Text.drop_front(); // ','
+ assert(!Text.empty() && isdigit(Text[0]) &&
+ "expected another modifier");
+ }
+ }
+ Parsed.push_back(Sub);
+ continue;
+ }
+ case MT_Diff: {
+ DiffPiece *Diff = New<DiffPiece>();
+ Text = Text.drop_front(); // '{'
+ Diff->Options[0] = parseDiagText(Text, true);
+ Text = Text.drop_front(); // '|'
+ Diff->Options[1] = parseDiagText(Text, true);
+
+ Text = Text.drop_front(); // '}'
+ Diff->Indexes[0] = parseModifier(Text);
+ Text = Text.drop_front(); // ','
+ Diff->Indexes[1] = parseModifier(Text);
+ Parsed.push_back(Diff);
+ continue;
+ }
+ case MT_S: {
+ SelectPiece *Select = New<SelectPiece>(ModType);
+ Select->Options.push_back(New<TextPiece>(""));
+ Select->Options.push_back(New<TextPiece>("s", "diagtext"));
+ Select->Index = parseModifier(Text);
+ Parsed.push_back(Select);
+ continue;
+ }
+ case MT_Q:
+ case MT_Placeholder:
+ case MT_ObjCClass:
+ case MT_ObjCInstance:
+ case MT_Ordinal: {
+ Parsed.push_back(New<PlaceholderPiece>(ModType, parseModifier(Text)));
+ continue;
+ }
+ }
+ }
+
+ return New<MultiPiece>(Parsed);
+}
+
+std::vector<std::string>
+DiagnosticTextBuilder::buildForDocumentation(StringRef Severity,
+ const Record *R) {
+ EvaluatingRecordGuard Guard(&EvaluatingRecord, R);
+ StringRef Text = R->getValueAsString("Text");
+
+ DiagText D(*this, Text);
+ TextPiece *Prefix = D.New<TextPiece>(Severity, Severity);
+ Prefix->Text += ": ";
+ auto *MP = dyn_cast<MultiPiece>(D.Root);
+ if (!MP) {
+ MP = D.New<MultiPiece>();
+ MP->Pieces.push_back(D.Root);
+ D.Root = MP;
+ }
+ MP->Pieces.insert(MP->Pieces.begin(), Prefix);
+ std::vector<std::string> Result;
+ DiagTextDocPrinter{*this, Result}.Visit(D.Root);
+ return Result;
+}
+
+std::string DiagnosticTextBuilder::buildForDefinition(const Record *R) {
+ EvaluatingRecordGuard Guard(&EvaluatingRecord, R);
+ StringRef Text = R->getValueAsString("Text");
+ DiagText D(*this, Text);
+ std::string Result;
+ DiagTextPrinter{*this, Result}.Visit(D.Root);
+ return Result;
+}
+
+} // namespace
+
//===----------------------------------------------------------------------===//
// Warning Tables (.inc file) generation.
//===----------------------------------------------------------------------===//
@@ -455,6 +1172,7 @@
return ClsName == "CLASS_REMARK";
}
+
/// ClangDiagsDefsEmitter - The top-level class emits .def files containing
/// declarations of Clang diagnostics.
namespace clang {
@@ -470,8 +1188,9 @@
OS << "#endif\n\n";
}
- const std::vector<Record*> &Diags =
- Records.getAllDerivedDefinitions("Diagnostic");
+ DiagnosticTextBuilder DiagTextBuilder(Records);
+
+ std::vector<Record *> Diags = Records.getAllDerivedDefinitions("Diagnostic");
std::vector<Record*> DiagGroups
= Records.getAllDerivedDefinitions("DiagGroup");
@@ -520,7 +1239,7 @@
// Description string.
OS << ", \"";
- OS.write_escaped(R.getValueAsString("Text")) << '"';
+ OS.write_escaped(DiagTextBuilder.buildForDefinition(&R)) << '"';
// Warning associated with the diagnostic. This is stored as an index into
// the alphabetically sorted warning table.
@@ -882,261 +1601,6 @@
namespace docs {
namespace {
-/// Diagnostic text, parsed into pieces.
-struct DiagText {
- struct Piece {
- // This type and its derived classes are move-only.
- Piece() {}
- Piece(Piece &&O) {}
- Piece &operator=(Piece &&O) { return *this; }
-
- virtual void print(std::vector<std::string> &RST) = 0;
- virtual ~Piece() {}
- };
- struct TextPiece : Piece {
- StringRef Role;
- std::string Text;
- void print(std::vector<std::string> &RST) override;
- };
- struct PlaceholderPiece : Piece {
- int Index;
- void print(std::vector<std::string> &RST) override;
- };
- struct SelectPiece : Piece {
- SelectPiece() {}
- SelectPiece(SelectPiece &&O) noexcept : Options(std::move(O.Options)) {}
- std::vector<DiagText> Options;
- void print(std::vector<std::string> &RST) override;
- };
-
- std::vector<std::unique_ptr<Piece>> Pieces;
-
- DiagText();
- DiagText(DiagText &&O) noexcept : Pieces(std::move(O.Pieces)) {}
-
- DiagText(StringRef Text);
- DiagText(StringRef Kind, StringRef Text);
-
- template<typename P> void add(P Piece) {
- Pieces.push_back(llvm::make_unique<P>(std::move(Piece)));
- }
- void print(std::vector<std::string> &RST);
-};
-
-DiagText parseDiagText(StringRef &Text, bool Nested = false) {
- DiagText Parsed;
-
- while (!Text.empty()) {
- size_t End = (size_t)-2;
- do
- End = Nested ? Text.find_first_of("%|}", End + 2)
- : Text.find_first_of('%', End + 2);
- while (End < Text.size() - 1 && Text[End] == '%' && Text[End + 1] == '%');
-
- if (End) {
- DiagText::TextPiece Piece;
- Piece.Role = "diagtext";
- Piece.Text = Text.slice(0, End);
- Parsed.add(std::move(Piece));
- Text = Text.slice(End, StringRef::npos);
- if (Text.empty()) break;
- }
-
- if (Text[0] == '|' || Text[0] == '}')
- break;
-
- // Drop the '%'.
- Text = Text.drop_front();
-
- // Extract the (optional) modifier.
- size_t ModLength = Text.find_first_of("0123456789{");
- StringRef Modifier = Text.slice(0, ModLength);
- Text = Text.slice(ModLength, StringRef::npos);
-
- // FIXME: Handle %ordinal here.
- if (Modifier == "select" || Modifier == "plural") {
- DiagText::SelectPiece Select;
- do {
- Text = Text.drop_front();
- if (Modifier == "plural")
- while (Text[0] != ':')
- Text = Text.drop_front();
- Select.Options.push_back(parseDiagText(Text, true));
- assert(!Text.empty() && "malformed %select");
- } while (Text.front() == '|');
- Parsed.add(std::move(Select));
-
- // Drop the trailing '}n'.
- Text = Text.drop_front(2);
- continue;
- }
-
- // For %diff, just take the second alternative (tree diagnostic). It would
- // be preferable to take the first one, and replace the $ with the suitable
- // placeholders.
- if (Modifier == "diff") {
- Text = Text.drop_front(); // '{'
- parseDiagText(Text, true);
- Text = Text.drop_front(); // '|'
-
- DiagText D = parseDiagText(Text, true);
- for (auto &P : D.Pieces)
- Parsed.Pieces.push_back(std::move(P));
-
- Text = Text.drop_front(4); // '}n,m'
- continue;
- }
-
- if (Modifier == "s") {
- Text = Text.drop_front();
- DiagText::SelectPiece Select;
- Select.Options.push_back(DiagText(""));
- Select.Options.push_back(DiagText("s"));
- Parsed.add(std::move(Select));
- continue;
- }
-
- assert(!Text.empty() && isdigit(Text[0]) && "malformed placeholder");
- DiagText::PlaceholderPiece Placeholder;
- Placeholder.Index = Text[0] - '0';
- Parsed.add(std::move(Placeholder));
- Text = Text.drop_front();
- continue;
- }
- return Parsed;
-}
-
-DiagText::DiagText() {}
-
-DiagText::DiagText(StringRef Text) : DiagText(parseDiagText(Text, false)) {}
-
-DiagText::DiagText(StringRef Kind, StringRef Text) : DiagText(parseDiagText(Text, false)) {
- TextPiece Prefix;
- Prefix.Role = Kind;
- Prefix.Text = Kind;
- Prefix.Text += ": ";
- Pieces.insert(Pieces.begin(),
- llvm::make_unique<TextPiece>(std::move(Prefix)));
-}
-
-void escapeRST(StringRef Str, std::string &Out) {
- for (auto K : Str) {
- if (StringRef("`*|_[]\\").count(K))
- Out.push_back('\\');
- Out.push_back(K);
- }
-}
-
-template<typename It> void padToSameLength(It Begin, It End) {
- size_t Width = 0;
- for (It I = Begin; I != End; ++I)
- Width = std::max(Width, I->size());
- for (It I = Begin; I != End; ++I)
- (*I) += std::string(Width - I->size(), ' ');
-}
-
-template<typename It> void makeTableRows(It Begin, It End) {
- if (Begin == End) return;
- padToSameLength(Begin, End);
- for (It I = Begin; I != End; ++I)
- *I = "|" + *I + "|";
-}
-
-void makeRowSeparator(std::string &Str) {
- for (char &K : Str)
- K = (K == '|' ? '+' : '-');
-}
-
-void DiagText::print(std::vector<std::string> &RST) {
- if (Pieces.empty()) {
- RST.push_back("");
- return;
- }
-
- if (Pieces.size() == 1)
- return Pieces[0]->print(RST);
-
- std::string EmptyLinePrefix;
- size_t Start = RST.size();
- bool HasMultipleLines = true;
- for (auto &P : Pieces) {
- std::vector<std::string> Lines;
- P->print(Lines);
- if (Lines.empty())
- continue;
-
- // We need a vertical separator if either this or the previous piece is a
- // multi-line piece, or this is the last piece.
- const char *Separator = (Lines.size() > 1 || HasMultipleLines) ? "|" : "";
- HasMultipleLines = Lines.size() > 1;
-
- if (Start + Lines.size() > RST.size())
- RST.resize(Start + Lines.size(), EmptyLinePrefix);
-
- padToSameLength(Lines.begin(), Lines.end());
- for (size_t I = 0; I != Lines.size(); ++I)
- RST[Start + I] += Separator + Lines[I];
- std::string Empty(Lines[0].size(), ' ');
- for (size_t I = Start + Lines.size(); I != RST.size(); ++I)
- RST[I] += Separator + Empty;
- EmptyLinePrefix += Separator + Empty;
- }
- for (size_t I = Start; I != RST.size(); ++I)
- RST[I] += "|";
- EmptyLinePrefix += "|";
-
- makeRowSeparator(EmptyLinePrefix);
- RST.insert(RST.begin() + Start, EmptyLinePrefix);
- RST.insert(RST.end(), EmptyLinePrefix);
-}
-
-void DiagText::TextPiece::print(std::vector<std::string> &RST) {
- RST.push_back("");
- auto &S = RST.back();
-
- StringRef T = Text;
- while (!T.empty() && T.front() == ' ') {
- RST.back() += " |nbsp| ";
- T = T.drop_front();
- }
-
- std::string Suffix;
- while (!T.empty() && T.back() == ' ') {
- Suffix += " |nbsp| ";
- T = T.drop_back();
- }
-
- if (!T.empty()) {
- S += ':';
- S += Role;
- S += ":`";
- escapeRST(T, S);
- S += '`';
- }
-
- S += Suffix;
-}
-
-void DiagText::PlaceholderPiece::print(std::vector<std::string> &RST) {
- RST.push_back(std::string(":placeholder:`") + char('A' + Index) + "`");
-}
-
-void DiagText::SelectPiece::print(std::vector<std::string> &RST) {
- std::vector<size_t> SeparatorIndexes;
- SeparatorIndexes.push_back(RST.size());
- RST.emplace_back();
- for (auto &O : Options) {
- O.print(RST);
- SeparatorIndexes.push_back(RST.size());
- RST.emplace_back();
- }
-
- makeTableRows(RST.begin() + SeparatorIndexes.front(),
- RST.begin() + SeparatorIndexes.back() + 1);
- for (size_t I : SeparatorIndexes)
- makeRowSeparator(RST[I]);
-}
-
bool isRemarkGroup(const Record *DiagGroup,
const std::map<std::string, GroupInfo> &DiagsInGroup) {
bool AnyRemarks = false, AnyNonRemarks = false;
@@ -1181,12 +1645,13 @@
OS << Str << "\n" << std::string(Str.size(), Kind) << "\n";
}
-void writeDiagnosticText(StringRef Role, StringRef Text, raw_ostream &OS) {
+void writeDiagnosticText(DiagnosticTextBuilder &Builder, const Record *R,
+ StringRef Role, raw_ostream &OS) {
+ StringRef Text = R->getValueAsString("Text");
if (Text == "%0")
OS << "The text of this diagnostic is not controlled by Clang.\n\n";
else {
- std::vector<std::string> Out;
- DiagText(Role, Text).print(Out);
+ std::vector<std::string> Out = Builder.buildForDocumentation(Role, R);
for (auto &Line : Out)
OS << Line << "\n";
OS << "\n";
@@ -1209,8 +1674,11 @@
OS << Documentation->getValueAsString("Intro") << "\n";
+ DiagnosticTextBuilder Builder(Records);
+
std::vector<Record*> Diags =
Records.getAllDerivedDefinitions("Diagnostic");
+
std::vector<Record*> DiagGroups =
Records.getAllDerivedDefinitions("DiagGroup");
llvm::sort(DiagGroups.begin(), DiagGroups.end(), diagGroupBeforeByName);
@@ -1300,7 +1768,8 @@
Severity[0] = tolower(Severity[0]);
if (Severity == "ignored")
Severity = IsRemarkGroup ? "remark" : "warning";
- writeDiagnosticText(Severity, D->getValueAsString("Text"), OS);
+
+ writeDiagnosticText(Builder, D, Severity, OS);
}
}
Index: test/lit.cfg.py
===================================================================
--- test/lit.cfg.py
+++ test/lit.cfg.py
@@ -57,7 +57,8 @@
tool_dirs = [config.clang_tools_dir, config.llvm_tools_dir]
tools = [
- 'c-index-test', 'clang-check', 'clang-diff', 'clang-format', 'opt',
+ 'c-index-test', 'clang-check', 'clang-diff', 'clang-format', 'clang-tblgen',
+ 'opt',
ToolSubst('%clang_func_map', command=FindTool(
'clang-func-mapping'), unresolved='ignore'),
]
Index: test/TableGen/text-substitution.td
===================================================================
--- /dev/null
+++ test/TableGen/text-substitution.td
@@ -0,0 +1,40 @@
+// RUN: clang-tblgen -gen-clang-diags-defs -I%S %s -o - 2>&1 | \
+// RUN: FileCheck --strict-whitespace %s
+include "DiagnosticBase.inc"
+
+def yes_no : TextSubstitution<"%select{yes|no}0">;
+def says_yes : TextSubstitution<"%1 says %sub{yes_no}0">;
+
+
+def sub_test_rewrite : TextSubstitution<
+ "SELECT! %select{one|two}3. "
+ "DIFF! %diff{$ is $|or not}0,1. "
+ "PLURAL! %plural{0:zero items|[1,2]:one or two item|:multiple items}2. "
+ "ORDINAL! %ordinal1. "
+ "S! item%s2. "
+ "Q! %q4. "
+ "PLACEHOLDER! %5."
+ "OBJCCLASS! %objcclass0. "
+ "OBJCINSTANCE! %objcinstance1. ">;
+
+// CHECK: DIAG(test_rewrite,
+// CHECK-SAME: SELECT! %select{one|two}2.
+// CHECK-SAME: DIFF! %diff{$ is $|or not}5,4.
+// CHECK-SAME: PLURAL! %plural{0:zero items|[1,2]:one or two item|:multiple items}3.
+// CHECK-SAME: ORDINAL! %ordinal4.
+// CHECK-SAME: S! item%s3.
+// CHECK-SAME: Q! %q1.
+// CHECK-SAME: PLACEHOLDER! %0.OBJCCLASS!
+// CHECK-SAME: %objcclass5. OBJCINSTANCE!
+// CHECK-SAME: %objcinstance4. DONE!",
+def test_rewrite: Error<"%sub{sub_test_rewrite}5,4,3,2,1,0 DONE!">;
+
+def test_sub_basic : Error<"%sub{yes_no}0">;
+// CHECK: test_sub_basic
+// CHECK-SAME: "%select{yes|no}0",
+
+def test_sub_nested : Error<"%sub{says_yes}2,4">;
+// CHECK: test_sub_nested
+// CHECK-SAME: "%4 says %select{yes|no}2",
+
+
Index: test/TableGen/emit-diag-docs.td
===================================================================
--- /dev/null
+++ test/TableGen/emit-diag-docs.td
@@ -0,0 +1,78 @@
+// RUN: clang-tblgen -gen-diag-docs -I%S %s -o - 2>&1 | \
+// RUN: FileCheck --strict-whitespace %s
+include "DiagnosticBase.inc"
+
+def MyGroup : DiagGroup<"MyGroupName">;
+
+def MyKinds : TextSubstitution<"%select{food|forests}0">;
+def MyGoodBad : TextSubstitution<"%select{good|bad}0">;
+def MySubNested : TextSubstitution<"%sub{MyGoodBad}1 %sub{MyKinds}2 are %sub{MyGoodBad}1 according to %0">;
+
+// CHECK: -WMyGroupName
+// CHECK: **Diagnostic text:**
+
+let Group = MyGroup in {
+
+// CHECK: |:warning:`warning:` |nbsp| :diagtext:`this is my diff text`|
+// CHECK-NEXT: +-----------------------------------------------------------+
+def CheckDiff : Warning<"%diff{$ is not $|this is my diff text}0,1">;
+
+
+// CHECK: |:warning:`warning:` |nbsp| :placeholder:`A` |nbsp| :diagtext:`is my modifier test` |nbsp| :placeholder:`B`|
+// CHECK-NEXT: +----------------------------------------------------------------------------------------------------------+
+def CheckModifier : Warning<"%0 is my modifier test %1">;
+
+
+// CHECK: |:warning:`warning:` |nbsp| :diagtext:`This is the` |nbsp| :placeholder:`A` |nbsp| :diagtext:`test I've written`|
+// CHECK-NEXT: +---------------------------------------------------------------------------------------------------------------+
+def CheckOrdinal : Warning<"This is the %ordinal0 test I've written">;
+
+// CHECK: |:warning:`warning:` |nbsp| :diagtext:`I wrote` |nbsp| |+----------------+| |nbsp| :diagtext:`tests`|
+// CHECK-NEXT: | ||:diagtext:`no` || |
+// CHECK-NEXT: | |+----------------+| |
+// CHECK-NEXT: | ||:diagtext:`one` || |
+// CHECK-NEXT: | |+----------------+| |
+// CHECK-NEXT: | ||:placeholder:`A`|| |
+// CHECK-NEXT: | |+----------------+| |
+// CHECK-NEXT: +------------------------------------------------------+------------------+-------------------------+
+def CheckPlural : Warning<"I wrote %plural{0:no|1:one|:%0}0 tests">;
+
+
+// CHECK: |:warning:`warning:` |nbsp| :diagtext:`bad type` |nbsp| :placeholder:`A`|
+// CHECK-NEXT: +-----------------------------------------------------------------------+
+def CheckQ : Warning<"bad type %q0">;
+
+
+// CHECK: |:warning:`warning:` |nbsp| :diagtext:`My test`|+-------------+| |nbsp| :diagtext:`are the best!`|
+// CHECK-NEXT: | || || |
+// CHECK-NEXT: | |+-------------+| |
+// CHECK-NEXT: | ||:diagtext:`s`|| |
+// CHECK-NEXT: | |+-------------+| |
+// CHECK-NEXT: +----------------------------------------------+---------------+---------------------------------+
+def CheckS : Warning<"My test%s0 are the best!">;
+
+
+// CHECK: |:warning:`warning:` |nbsp| :diagtext:`this is my select test:` |nbsp| |+---------------+|
+// CHECK-NEXT: | ||:diagtext:`one`||
+// CHECK-NEXT: | |+---------------+|
+// CHECK-NEXT: | ||:diagtext:`two`||
+// CHECK-NEXT: | |+---------------+|
+// CHECK-NEXT: +----------------------------------------------------------------------+-----------------+
+def CheckSelect : Warning<"this is my select test: %select{one|two}0 and it is %select{good|bad}1">;
+
+
+// CHECK: +-------------------------------------------------------+------------------+--------+---------------------+-------------------------------+------------------+--------------------------------------------------------+
+// CHECK-NEXT: |:warning:`warning:` |nbsp| :diagtext:`They say` |nbsp| |+----------------+| |nbsp| |+-------------------+| |nbsp| :diagtext:`are` |nbsp| |+----------------+| |nbsp| :diagtext:`according to` |nbsp| :placeholder:`D`|
+// CHECK-NEXT: | ||:diagtext:`good`|| ||:diagtext:`food` || ||:diagtext:`good`|| |
+// CHECK-NEXT: | |+----------------+| |+-------------------+| |+----------------+| |
+// CHECK-NEXT: | ||:diagtext:`bad` || ||:diagtext:`forests`|| ||:diagtext:`bad` || |
+// CHECK-NEXT: | |+----------------+| |+-------------------+| |+----------------+| |
+// CHECK-NEXT: +-------------------------------------------------------+------------------+--------+---------------------+-------------------------------+------------------+--------------------------------------------------------+
+def CheckSubstitution : Warning<"They say %sub{MySubNested}3,1,0">;
+
+
+// CHECK: |:warning:`warning:` |nbsp| :diagtext:`this is my warning text`|
+// CHECK-NEXT: +--------------------------------------------------------------+
+def CheckText : Warning<"this is my warning text">;
+
+}
Index: test/TableGen/DiagnosticDocs.inc
===================================================================
--- /dev/null
+++ test/TableGen/DiagnosticDocs.inc
@@ -0,0 +1,75 @@
+
+def GlobalDocumentation {
+ code Intro =[{..
+ -------------------------------------------------------------------
+ NOTE: This file is automatically generated by running clang-tblgen
+ -gen-diag-docs. Do not edit this file by hand!!
+ -------------------------------------------------------------------
+
+.. Add custom CSS to output. FIXME: This should be put into <head> rather
+ than the start of <body>.
+.. raw:: html
+
+ <style>
+ table.docutils {
+ width: 1px;
+ }
+ table.docutils td {
+ border: none;
+ padding: 0 0 0 0.2em;
+ vertical-align: middle;
+ white-space: nowrap;
+ width: 1px;
+ font-family: monospace;
+ }
+ table.docutils tr + tr {
+ border-top: 0.2em solid #aaa;
+ }
+ .error {
+ font-family: monospace;
+ font-weight: bold;
+ color: #c00;
+ }
+ .warning {
+ font-family: monospace;
+ font-weight: bold;
+ color: #80a;
+ }
+ .remark {
+ font-family: monospace;
+ font-weight: bold;
+ color: #00c;
+ }
+ .diagtext {
+ font-family: monospace;
+ font-weight: bold;
+ }
+ </style>
+
+.. FIXME: rST doesn't support formatting this, so we format all <td> elements
+ as monospace font face instead.
+.. |nbsp| unicode:: 0xA0
+ :trim:
+
+.. Roles generated by clang-tblgen.
+.. role:: error
+.. role:: warning
+.. role:: remark
+.. role:: diagtext
+.. role:: placeholder(emphasis)
+
+=========================
+Diagnostic flags in Clang
+=========================
+.. contents::
+ :local:
+
+Introduction
+============
+
+This page lists the diagnostic flags currently supported by Clang.
+
+Diagnostic flags
+================
+}];
+}
Index: test/TableGen/DiagnosticBase.inc
===================================================================
--- test/TableGen/DiagnosticBase.inc
+++ test/TableGen/DiagnosticBase.inc
@@ -1,35 +1,130 @@
-// Define the diagnostic mappings.
-class DiagMapping;
-def MAP_IGNORE : DiagMapping;
-def MAP_WARNING : DiagMapping;
-def MAP_ERROR : DiagMapping;
-def MAP_FATAL : DiagMapping;
+//===--- DiagnosticBase.inc - A test file mimicking Diagnostic.td ---------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the TableGen core definitions for the diagnostics
+// and diagnostic control.
+//
+//===----------------------------------------------------------------------===//
+
+// See the Internals Manual, section The Diagnostics Subsystem for an overview.
+
+// Define the diagnostic severities.
+class Severity<string N> {
+ string Name = N;
+}
+def SEV_Ignored : Severity<"Ignored">;
+def SEV_Remark : Severity<"Remark">;
+def SEV_Warning : Severity<"Warning">;
+def SEV_Error : Severity<"Error">;
+def SEV_Fatal : Severity<"Fatal">;
// Define the diagnostic classes.
class DiagClass;
def CLASS_NOTE : DiagClass;
+def CLASS_REMARK : DiagClass;
def CLASS_WARNING : DiagClass;
def CLASS_EXTENSION : DiagClass;
def CLASS_ERROR : DiagClass;
+// Responses to a diagnostic in a SFINAE context.
+class SFINAEResponse;
+def SFINAE_SubstitutionFailure : SFINAEResponse;
+def SFINAE_Suppress : SFINAEResponse;
+def SFINAE_Report : SFINAEResponse;
+def SFINAE_AccessControl : SFINAEResponse;
+
+// Textual substitutions which may be performed on the text of diagnostics
+class TextSubstitution<string Text> {
+ string Substitution = Text;
+ // TODO: These are only here to allow substitutions to be declared inline with
+ // diagnostics
+ string Component = "";
+ string CategoryName = "";
+}
+
+// Diagnostic Categories. These can be applied to groups or individual
+// diagnostics to specify a category.
+class DiagCategory<string Name> {
+ string CategoryName = Name;
+}
+
+// Diagnostic Groups.
class DiagGroup<string Name, list<DiagGroup> subgroups = []> {
string GroupName = Name;
list<DiagGroup> SubGroups = subgroups;
string CategoryName = "";
+ code Documentation = [{}];
}
class InGroup<DiagGroup G> { DiagGroup Group = G; }
+//class IsGroup<string Name> { DiagGroup Group = DiagGroup<Name>; }
+
+include "DiagnosticDocs.inc"
// All diagnostics emitted by the compiler are an indirect subclass of this.
-class Diagnostic<string text, DiagClass DC, DiagMapping defaultmapping> {
+class Diagnostic<string text, DiagClass DC, Severity defaultmapping> {
+ /// Component is specified by the file with a big let directive.
+ string Component = ?;
string Text = text;
DiagClass Class = DC;
- DiagMapping DefaultMapping = defaultmapping;
+ SFINAEResponse SFINAE = SFINAE_Suppress;
+ bit AccessControl = 0;
+ bit WarningNoWerror = 0;
+ bit ShowInSystemHeader = 0;
+ Severity DefaultSeverity = defaultmapping;
DiagGroup Group;
string CategoryName = "";
}
-class Error<string str> : Diagnostic<str, CLASS_ERROR, MAP_ERROR>;
-class Warning<string str> : Diagnostic<str, CLASS_WARNING, MAP_WARNING>;
-class Extension<string str> : Diagnostic<str, CLASS_EXTENSION, MAP_IGNORE>;
-class ExtWarn<string str> : Diagnostic<str, CLASS_EXTENSION, MAP_WARNING>;
-class Note<string str> : Diagnostic<str, CLASS_NOTE, MAP_FATAL/*ignored*/>;
+class SFINAEFailure {
+ SFINAEResponse SFINAE = SFINAE_SubstitutionFailure;
+}
+class NoSFINAE {
+ SFINAEResponse SFINAE = SFINAE_Report;
+}
+class AccessControl {
+ SFINAEResponse SFINAE = SFINAE_AccessControl;
+}
+
+class ShowInSystemHeader {
+ bit ShowInSystemHeader = 1;
+}
+
+class SuppressInSystemHeader {
+ bit ShowInSystemHeader = 0;
+}
+
+// FIXME: ExtWarn and Extension should also be SFINAEFailure by default.
+class Error<string str> : Diagnostic<str, CLASS_ERROR, SEV_Error>, SFINAEFailure {
+ bit ShowInSystemHeader = 1;
+}
+// Warnings default to on (but can be default-off'd with DefaultIgnore).
+// This is used for warnings about questionable code; warnings about
+// accepted language extensions should use Extension or ExtWarn below instead.
+class Warning<string str> : Diagnostic<str, CLASS_WARNING, SEV_Warning>;
+// Remarks can be turned on with -R flags and provide commentary, e.g. on
+// optimizer decisions.
+class Remark<string str> : Diagnostic<str, CLASS_REMARK, SEV_Ignored>;
+// Extensions are warnings about accepted language extensions.
+// Extension warnings are default-off but enabled by -pedantic.
+class Extension<string str> : Diagnostic<str, CLASS_EXTENSION, SEV_Ignored>;
+// ExtWarns are warnings about accepted language extensions.
+// ExtWarn warnings are default-on.
+class ExtWarn<string str> : Diagnostic<str, CLASS_EXTENSION, SEV_Warning>;
+// Notes can provide supplementary information on errors, warnings, and remarks.
+class Note<string str> : Diagnostic<str, CLASS_NOTE, SEV_Fatal/*ignored*/>;
+
+
+class DefaultIgnore { Severity DefaultSeverity = SEV_Ignored; }
+class DefaultWarn { Severity DefaultSeverity = SEV_Warning; }
+class DefaultError { Severity DefaultSeverity = SEV_Error; }
+class DefaultFatal { Severity DefaultSeverity = SEV_Fatal; }
+class DefaultWarnNoWerror {
+ bit WarningNoWerror = 1;
+}
+class DefaultRemark { Severity DefaultSeverity = SEV_Remark; }
Index: test/SemaCXX/cxx98-compat.cpp
===================================================================
--- test/SemaCXX/cxx98-compat.cpp
+++ test/SemaCXX/cxx98-compat.cpp
@@ -239,13 +239,13 @@
~NonTrivDtor(); // expected-note 2{{user-provided destructor}}
};
union BadUnion {
- NonTrivCtor ntc; // expected-warning {{union member 'ntc' with a non-trivial constructor is incompatible with C++98}}
+ NonTrivCtor ntc; // expected-warning {{union member 'ntc' with a non-trivial default constructor is incompatible with C++98}}
NonTrivCopy ntcp; // expected-warning {{union member 'ntcp' with a non-trivial copy constructor is incompatible with C++98}}
NonTrivDtor ntd; // expected-warning {{union member 'ntd' with a non-trivial destructor is incompatible with C++98}}
};
struct Wrap {
struct {
- NonTrivCtor ntc; // expected-warning {{anonymous struct member 'ntc' with a non-trivial constructor is incompatible with C++98}}
+ NonTrivCtor ntc; // expected-warning {{anonymous struct member 'ntc' with a non-trivial default constructor is incompatible with C++98}}
NonTrivCopy ntcp; // expected-warning {{anonymous struct member 'ntcp' with a non-trivial copy constructor is incompatible with C++98}}
NonTrivDtor ntd; // expected-warning {{anonymous struct member 'ntd' with a non-trivial destructor is incompatible with C++98}}
};
@@ -346,7 +346,7 @@
};
union S {
- X x; // expected-warning{{union member 'x' with a non-trivial constructor is incompatible with C++98}}
+ X x; // expected-warning{{union member 'x' with a non-trivial default constructor is incompatible with C++98}}
};
}
Index: test/SemaCXX/anonymous-struct.cpp
===================================================================
--- test/SemaCXX/anonymous-struct.cpp
+++ test/SemaCXX/anonymous-struct.cpp
@@ -16,7 +16,7 @@
struct {
S x;
#if __cplusplus <= 199711L
- // expected-error@-2 {{anonymous struct member 'x' has a non-trivial constructor}}
+ // expected-error@-2 {{anonymous struct member 'x' has a non-trivial default constructor}}
#endif
};
static struct {
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp
+++ lib/Sema/SemaOverload.cpp
@@ -9327,36 +9327,43 @@
oc_function,
oc_method,
oc_constructor,
- oc_function_template,
- oc_method_template,
- oc_constructor_template,
oc_implicit_default_constructor,
oc_implicit_copy_constructor,
oc_implicit_move_constructor,
oc_implicit_copy_assignment,
oc_implicit_move_assignment,
- oc_inherited_constructor,
- oc_inherited_constructor_template
+ oc_inherited_constructor
};
-static OverloadCandidateKind
+enum OverloadCandidateSelect {
+ ocs_non_template,
+ ocs_template,
+ ocs_described_template,
+};
+
+static std::pair<OverloadCandidateKind, OverloadCandidateSelect>
ClassifyOverloadCandidate(Sema &S, NamedDecl *Found, FunctionDecl *Fn,
std::string &Description) {
bool isTemplate = false;
-
if (FunctionTemplateDecl *FunTmpl = Fn->getPrimaryTemplate()) {
isTemplate = true;
Description = S.getTemplateArgumentBindingsText(
FunTmpl->getTemplateParameters(), *Fn->getTemplateSpecializationArgs());
}
+ OverloadCandidateSelect Select = [&]() {
+ if (!Description.empty())
+ return ocs_described_template;
+ return isTemplate ? ocs_template : ocs_non_template;
+ }();
+
+ OverloadCandidateKind Kind = [&]() {
if (CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(Fn)) {
if (!Ctor->isImplicit()) {
if (isa<ConstructorUsingShadowDecl>(Found))
- return isTemplate ? oc_inherited_constructor_template
- : oc_inherited_constructor;
+ return oc_inherited_constructor;
else
- return isTemplate ? oc_constructor_template : oc_constructor;
+ return oc_constructor;
}
if (Ctor->isDefaultConstructor())
@@ -9374,7 +9381,7 @@
// This actually gets spelled 'candidate function' for now, but
// it doesn't hurt to split it out.
if (!Meth->isImplicit())
- return isTemplate ? oc_method_template : oc_method;
+ return oc_method;
if (Meth->isMoveAssignmentOperator())
return oc_implicit_move_assignment;
@@ -9386,7 +9393,10 @@
return oc_method;
}
- return isTemplate ? oc_function_template : oc_function;
+ return oc_function;
+ }();
+
+ return std::make_pair(Kind, Select);
}
void MaybeEmitInheritedConstructorNote(Sema &S, Decl *FoundDecl) {
@@ -9478,9 +9488,11 @@
return;
std::string FnDesc;
- OverloadCandidateKind K = ClassifyOverloadCandidate(*this, Found, Fn, FnDesc);
+ std::pair<OverloadCandidateKind, OverloadCandidateSelect> KSPair =
+ ClassifyOverloadCandidate(*this, Found, Fn, FnDesc);
PartialDiagnostic PD = PDiag(diag::note_ovl_candidate)
- << (unsigned) K << Fn << FnDesc;
+ << (unsigned)KSPair.first << (unsigned)KSPair.second
+ << Fn << FnDesc;
HandleFunctionTypeMismatch(PD, Fn->getType(), DestType);
Diag(Fn->getLocation(), PD);
@@ -9554,7 +9566,7 @@
}
std::string FnDesc;
- OverloadCandidateKind FnKind =
+ std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =
ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, FnDesc);
Expr *FromExpr = Conv.Bad.FromExpr;
@@ -9569,9 +9581,9 @@
DeclarationName Name = cast<OverloadExpr>(E)->getName();
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_overload)
- << (unsigned) FnKind << FnDesc
- << (FromExpr ? FromExpr->getSourceRange() : SourceRange())
- << ToTy << Name << I+1;
+ << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc
+ << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << ToTy
+ << Name << I + 1;
MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
return;
}
@@ -9598,9 +9610,8 @@
if (FromQs.getAddressSpace() != ToQs.getAddressSpace()) {
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_addrspace)
- << (unsigned) FnKind << FnDesc
- << (FromExpr ? FromExpr->getSourceRange() : SourceRange())
- << FromTy
+ << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc
+ << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << FromTy
<< FromQs.getAddressSpaceAttributePrintValue()
<< ToQs.getAddressSpaceAttributePrintValue()
<< (unsigned)isObjectArgument << I + 1;
@@ -9610,31 +9621,29 @@
if (FromQs.getObjCLifetime() != ToQs.getObjCLifetime()) {
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_ownership)
- << (unsigned) FnKind << FnDesc
- << (FromExpr ? FromExpr->getSourceRange() : SourceRange())
- << FromTy
+ << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc
+ << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << FromTy
<< FromQs.getObjCLifetime() << ToQs.getObjCLifetime()
<< (unsigned)isObjectArgument << I + 1;
MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
return;
}
if (FromQs.getObjCGCAttr() != ToQs.getObjCGCAttr()) {
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_gc)
- << (unsigned) FnKind << FnDesc
- << (FromExpr ? FromExpr->getSourceRange() : SourceRange())
- << FromTy
+ << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc
+ << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << FromTy
<< FromQs.getObjCGCAttr() << ToQs.getObjCGCAttr()
<< (unsigned)isObjectArgument << I + 1;
MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
return;
}
if (FromQs.hasUnaligned() != ToQs.hasUnaligned()) {
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_unaligned)
- << (unsigned) FnKind << FnDesc
- << (FromExpr ? FromExpr->getSourceRange() : SourceRange())
- << FromTy << FromQs.hasUnaligned() << I+1;
+ << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc
+ << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << FromTy
+ << FromQs.hasUnaligned() << I + 1;
MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
return;
}
@@ -9644,14 +9653,14 @@
if (isObjectArgument) {
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_cvr_this)
- << (unsigned) FnKind << FnDesc
- << (FromExpr ? FromExpr->getSourceRange() : SourceRange())
- << FromTy << (CVR - 1);
+ << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc
+ << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << FromTy
+ << (CVR - 1);
} else {
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_cvr)
- << (unsigned) FnKind << FnDesc
- << (FromExpr ? FromExpr->getSourceRange() : SourceRange())
- << FromTy << (CVR - 1) << I+1;
+ << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc
+ << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << FromTy
+ << (CVR - 1) << I + 1;
}
MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
return;
@@ -9661,9 +9670,9 @@
// telling the user that it has type void is not useful.
if (FromExpr && isa<InitListExpr>(FromExpr)) {
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_list_argument)
- << (unsigned) FnKind << FnDesc
- << (FromExpr ? FromExpr->getSourceRange() : SourceRange())
- << FromTy << ToTy << (unsigned) isObjectArgument << I+1;
+ << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc
+ << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << FromTy
+ << ToTy << (unsigned)isObjectArgument << I + 1;
MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
return;
}
@@ -9677,9 +9686,9 @@
if (TempFromTy->isIncompleteType()) {
// Emit the generic diagnostic and, optionally, add the hints to it.
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_conv_incomplete)
- << (unsigned) FnKind << FnDesc
- << (FromExpr ? FromExpr->getSourceRange() : SourceRange())
- << FromTy << ToTy << (unsigned) isObjectArgument << I+1
+ << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc
+ << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << FromTy
+ << ToTy << (unsigned)isObjectArgument << I + 1
<< (unsigned)(Cand->Fix.Kind);
MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
@@ -9718,21 +9727,19 @@
ToTy.getNonReferenceType().getCanonicalType() ==
FromTy.getNonReferenceType().getCanonicalType()) {
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_lvalue)
- << (unsigned) FnKind << FnDesc
- << (FromExpr ? FromExpr->getSourceRange() : SourceRange())
- << (unsigned) isObjectArgument << I + 1;
+ << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc
+ << (unsigned)isObjectArgument << I + 1
+ << (FromExpr ? FromExpr->getSourceRange() : SourceRange());
MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
return;
}
}
if (BaseToDerivedConversion) {
- S.Diag(Fn->getLocation(),
- diag::note_ovl_candidate_bad_base_to_derived_conv)
- << (unsigned) FnKind << FnDesc
+ S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_base_to_derived_conv)
+ << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc
<< (FromExpr ? FromExpr->getSourceRange() : SourceRange())
- << (BaseToDerivedConversion - 1)
- << FromTy << ToTy << I+1;
+ << (BaseToDerivedConversion - 1) << FromTy << ToTy << I + 1;
MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
return;
}
@@ -9743,8 +9750,8 @@
Qualifiers ToQs = CToTy.getQualifiers();
if (FromQs.getObjCLifetime() != ToQs.getObjCLifetime()) {
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_arc_conv)
- << (unsigned) FnKind << FnDesc
- << (FromExpr ? FromExpr->getSourceRange() : SourceRange())
+ << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second
+ << FnDesc << (FromExpr ? FromExpr->getSourceRange() : SourceRange())
<< FromTy << ToTy << (unsigned)isObjectArgument << I + 1;
MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
return;
@@ -9757,9 +9764,9 @@
// Emit the generic diagnostic and, optionally, add the hints to it.
PartialDiagnostic FDiag = S.PDiag(diag::note_ovl_candidate_bad_conv);
- FDiag << (unsigned) FnKind << FnDesc
- << (FromExpr ? FromExpr->getSourceRange() : SourceRange())
- << FromTy << ToTy << (unsigned) isObjectArgument << I + 1
+ FDiag << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc
+ << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << FromTy
+ << ToTy << (unsigned)isObjectArgument << I + 1
<< (unsigned)(Cand->Fix.Kind);
// If we can fix the conversion, suggest the FixIts.
@@ -9833,17 +9840,17 @@
}
std::string Description;
- OverloadCandidateKind FnKind =
+ std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =
ClassifyOverloadCandidate(S, Found, Fn, Description);
if (modeCount == 1 && Fn->getParamDecl(0)->getDeclName())
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity_one)
- << (unsigned) FnKind << (Fn->getDescribedFunctionTemplate() != nullptr)
- << mode << Fn->getParamDecl(0) << NumFormalArgs;
+ << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second
+ << Description << mode << Fn->getParamDecl(0) << NumFormalArgs;
else
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity)
- << (unsigned) FnKind << (Fn->getDescribedFunctionTemplate() != nullptr)
- << mode << modeCount << NumFormalArgs;
+ << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second
+ << Description << mode << modeCount << NumFormalArgs;
MaybeEmitInheritedConstructorNote(S, Found);
}
@@ -10118,20 +10125,22 @@
CalleeTarget = S.IdentifyCUDATarget(Callee);
std::string FnDesc;
- OverloadCandidateKind FnKind =
+ std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =
ClassifyOverloadCandidate(S, Cand->FoundDecl, Callee, FnDesc);
S.Diag(Callee->getLocation(), diag::note_ovl_candidate_bad_target)
- << (unsigned)FnKind << CalleeTarget << CallerTarget;
+ << (unsigned)FnKindPair.first << (unsigned)ocs_non_template
+ << FnDesc /* Ignored */
+ << CalleeTarget << CallerTarget;
// This could be an implicit constructor for which we could not infer the
// target due to a collsion. Diagnose that case.
CXXMethodDecl *Meth = dyn_cast<CXXMethodDecl>(Callee);
if (Meth != nullptr && Meth->isImplicit()) {
CXXRecordDecl *ParentClass = Meth->getParent();
Sema::CXXSpecialMember CSM;
- switch (FnKind) {
+ switch (FnKindPair.first) {
default:
return;
case oc_implicit_default_constructor:
@@ -10203,11 +10212,11 @@
if (Cand->Viable) {
if (Fn->isDeleted() || S.isFunctionConsideredUnavailable(Fn)) {
std::string FnDesc;
- OverloadCandidateKind FnKind =
+ std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =
ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, FnDesc);
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_deleted)
- << FnKind << FnDesc
+ << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc
<< (Fn->isDeleted() ? (Fn->isDeletedAsWritten() ? 1 : 2) : 0);
MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
return;
@@ -11104,7 +11113,7 @@
S.PDiag(diag::err_addr_ovl_ambiguous)
<< Matches[0].second->getDeclName(),
S.PDiag(diag::note_ovl_candidate)
- << (unsigned)oc_function_template,
+ << (unsigned)oc_function << (unsigned)ocs_described_template,
Complain, TargetFunctionType);
if (Result != MatchesCopy.end()) {
Index: lib/Basic/Diagnostic.cpp
===================================================================
--- lib/Basic/Diagnostic.cpp
+++ lib/Basic/Diagnostic.cpp
@@ -580,6 +580,8 @@
// Skip over 'ValNo' |'s.
while (ValNo) {
const char *NextVal = ScanFormat(Argument, ArgumentEnd, '|');
+ if (NextVal == ArgumentEnd)
+ llvm::errs() << "WITH ARGUMENT: " << Argument;
assert(NextVal != ArgumentEnd && "Value for integer select modifier was"
" larger than the number of options in the diagnostic string!");
Argument = NextVal+1; // Skip this string.
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -1622,13 +1622,16 @@
"overrides of %0 in subclasses are not available in the "
"%select{constructor|destructor}1 of %2">;
+def select_special_member_kind : TextSubstitution<
+ "%select{default constructor|copy constructor|move constructor|"
+ "copy assignment operator|move assignment operator|destructor}0">;
+
def note_member_declared_at : Note<"member is declared here">;
def note_ivar_decl : Note<"instance variable is declared here">;
def note_bitfield_decl : Note<"bit-field is declared here">;
def note_implicit_param_decl : Note<"%0 is an implicit parameter">;
def note_member_synthesized_at : Note<
- "in implicit %select{default constructor|copy constructor|move constructor|"
- "copy assignment operator|move assignment operator|destructor}0 for %1 "
+ "in implicit %sub{select_special_member_kind}0 for %1 "
"first required here">;
def err_missing_default_ctor : Error<
"%select{constructor for %1 must explicitly initialize the|"
@@ -1641,12 +1644,10 @@
def err_illegal_union_or_anon_struct_member : Error<
"%select{anonymous struct|union}0 member %1 has a non-trivial "
- "%select{constructor|copy constructor|move constructor|copy assignment "
- "operator|move assignment operator|destructor}2">;
+ "%sub{select_special_member_kind}2">;
def warn_cxx98_compat_nontrivial_union_or_anon_struct_member : Warning<
"%select{anonymous struct|union}0 member %1 with a non-trivial "
- "%select{constructor|copy constructor|move constructor|copy assignment "
- "operator|move assignment operator|destructor}2 is incompatible with C++98">,
+ "%sub{select_special_member_kind}2 is incompatible with C++98">,
InGroup<CXX98Compat>, DefaultIgnore;
def note_nontrivial_virtual_dtor : Note<
@@ -1665,8 +1666,7 @@
"%select{base class|field|an object}0 of type %3">;
def note_nontrivial_user_provided : Note<
"because %select{base class of |field of |}0type %1 has a user-provided "
- "%select{default constructor|copy constructor|move constructor|"
- "copy assignment operator|move assignment operator|destructor}2">;
+ "%sub{select_special_member_kind}2">;
def note_nontrivial_in_class_init : Note<
"because field %0 has an initializer">;
def note_nontrivial_param_type : Note<
@@ -1733,9 +1733,7 @@
// C++ implicit special member functions
def note_in_declaration_of_implicit_special_member : Note<
- "while declaring the implicit "
- "%select{default constructor|copy constructor|move constructor|"
- "copy assignment operator|move assignment operator|destructor}1"
+ "while declaring the implicit %sub{select_special_member_kind}1"
" for %0">;
// C++ constructors
@@ -3525,27 +3523,29 @@
def note_ovl_too_many_candidates : Note<
"remaining %0 candidate%s0 omitted; "
"pass -fshow-overloads=all to show them">;
-def note_ovl_candidate : Note<"candidate "
+
+def select_ovl_candidate_kind : TextSubstitution<
"%select{function|function|constructor|"
- "function |function |constructor |"
- "is the implicit default constructor|"
- "is the implicit copy constructor|"
- "is the implicit move constructor|"
- "is the implicit copy assignment operator|"
- "is the implicit move assignment operator|"
- "inherited constructor|"
- "inherited constructor }0%2"
- "%select{| has different class%diff{ (expected $ but has $)|}4,5"
- "| has different number of parameters (expected %4 but has %5)"
- "| has type mismatch at %ordinal4 parameter"
- "%diff{ (expected $ but has $)|}5,6"
- "| has different return type%diff{ ($ expected but has $)|}4,5"
+ "constructor (the implicit default constructor)|"
+ "constructor (the implicit copy constructor)|"
+ "constructor (the implicit move constructor)|"
+ "function (the implicit copy assignment operator)|"
+ "function (the implicit move assignment operator)|"
+ "inherited constructor}0%select{| template| %2}1">;
+
+def note_ovl_candidate : Note<
+ "candidate %sub{select_ovl_candidate_kind}0,1,3"
+ "%select{| has different class%diff{ (expected $ but has $)|}5,6"
+ "| has different number of parameters (expected %5 but has %6)"
+ "| has type mismatch at %ordinal5 parameter"
+ "%diff{ (expected $ but has $)|}6,7"
+ "| has different return type%diff{ ($ expected but has $)|}5,6"
"| has different qualifiers (expected "
"%select{none|const|restrict|const and restrict|volatile|const and volatile"
- "|volatile and restrict|const, volatile, and restrict}4 but found "
+ "|volatile and restrict|const, volatile, and restrict}5 but found "
"%select{none|const|restrict|const and restrict|volatile|const and volatile"
- "|volatile and restrict|const, volatile, and restrict}5)"
- "| has different exception specification}3">;
+ "|volatile and restrict|const, volatile, and restrict}6)"
+ "| has different exception specification}4">;
def note_ovl_candidate_inherited_constructor : Note<
"constructor from base class %0 inherited here">;
@@ -3615,233 +3615,99 @@
// Note that we don't treat templates differently for this diagnostic.
def note_ovl_candidate_arity : Note<"candidate "
- "%select{function|function|constructor|function|function|constructor|"
- "constructor (the implicit default constructor)|"
- "constructor (the implicit copy constructor)|"
- "constructor (the implicit move constructor)|"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)|"
- "inherited constructor|"
- "inherited constructor}0 %select{|template }1"
- "not viable: requires%select{ at least| at most|}2 %3 argument%s3, but %4 "
- "%plural{1:was|:were}4 provided">;
+ "%sub{select_ovl_candidate_kind}0,1,2 not viable: "
+ "requires%select{ at least| at most|}3 %4 argument%s4, but %5 "
+ "%plural{1:was|:were}5 provided">;
def note_ovl_candidate_arity_one : Note<"candidate "
- "%select{function|function|constructor|function|function|constructor|"
- "constructor (the implicit default constructor)|"
- "constructor (the implicit copy constructor)|"
- "constructor (the implicit move constructor)|"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)|"
- "inherited constructor|"
- "inherited constructor}0 %select{|template }1not viable: "
- "%select{requires at least|allows at most single|requires single}2 "
- "argument %3, but %plural{0:no|:%4}4 arguments were provided">;
+ "%sub{select_ovl_candidate_kind}0,1,2 not viable: "
+ "%select{requires at least|allows at most single|requires single}3 "
+ "argument %4, but %plural{0:no|:%5}5 arguments were provided">;
def note_ovl_candidate_deleted : Note<
- "candidate %select{function|function|constructor|"
- "function |function |constructor |"
- "constructor (the implicit default constructor)|"
- "constructor (the implicit copy constructor)|"
- "constructor (the implicit move constructor)|"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)|"
- "inherited constructor|"
- "inherited constructor }0%1 has been "
+ "candidate %sub{select_ovl_candidate_kind}0,1,2 has been "
"%select{explicitly made unavailable|explicitly deleted|"
- "implicitly deleted}2">;
+ "implicitly deleted}3">;
// Giving the index of the bad argument really clutters this message, and
// it's relatively unimportant because 1) it's generally obvious which
// argument(s) are of the given object type and 2) the fix is usually
// to complete the type, which doesn't involve changes to the call line
// anyway. If people complain, we can change it.
-def note_ovl_candidate_bad_conv_incomplete : Note<"candidate "
- "%select{function|function|constructor|"
- "function |function |constructor |"
- "constructor (the implicit default constructor)|"
- "constructor (the implicit copy constructor)|"
- "constructor (the implicit move constructor)|"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)|"
- "inherited constructor|"
- "inherited constructor }0%1 "
- "not viable: cannot convert argument of incomplete type "
- "%diff{$ to $|to parameter type}2,3 for "
- "%select{%ordinal5 argument|object argument}4"
+def note_ovl_candidate_bad_conv_incomplete : Note<
+ "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
+ "cannot convert argument of incomplete type "
+ "%diff{$ to $|to parameter type}3,4 for "
+ "%select{%ordinal6 argument|object argument}5"
"%select{|; dereference the argument with *|"
"; take the address of the argument with &|"
"; remove *|"
- "; remove &}6">;
-def note_ovl_candidate_bad_list_argument : Note<"candidate "
- "%select{function|function|constructor|"
- "function |function |constructor |"
- "constructor (the implicit default constructor)|"
- "constructor (the implicit copy constructor)|"
- "constructor (the implicit move constructor)|"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)|"
- "inherited constructor|"
- "inherited constructor }0%1 "
- "not viable: cannot convert initializer list argument to %3">;
-def note_ovl_candidate_bad_overload : Note<"candidate "
- "%select{function|function|constructor|"
- "function |function |constructor |"
- "constructor (the implicit default constructor)|"
- "constructor (the implicit copy constructor)|"
- "constructor (the implicit move constructor)|"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)|"
- "inherited constructor|"
- "inherited constructor }0%1"
- " not viable: no overload of %3 matching %2 for %ordinal4 argument">;
-def note_ovl_candidate_bad_conv : Note<"candidate "
- "%select{function|function|constructor|"
- "function |function |constructor |"
- "constructor (the implicit default constructor)|"
- "constructor (the implicit copy constructor)|"
- "constructor (the implicit move constructor)|"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)|"
- "inherited constructor|"
- "inherited constructor }0%1"
- " not viable: no known conversion "
- "%diff{from $ to $|from argument type to parameter type}2,3 for "
- "%select{%ordinal5 argument|object argument}4"
+ "; remove &}7">;
+def note_ovl_candidate_bad_list_argument : Note<
+ "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
+ "cannot convert initializer list argument to %4">;
+def note_ovl_candidate_bad_overload : Note<
+ "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
+ "no overload of %4 matching %3 for %ordinal5 argument">;
+def note_ovl_candidate_bad_conv : Note<
+ "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
+ "no known conversion "
+ "%diff{from $ to $|from argument type to parameter type}3,4 for "
+ "%select{%ordinal6 argument|object argument}5"
"%select{|; dereference the argument with *|"
"; take the address of the argument with &|"
"; remove *|"
- "; remove &}6">;
-def note_ovl_candidate_bad_arc_conv : Note<"candidate "
- "%select{function|function|constructor|"
- "function |function |constructor |"
- "constructor (the implicit default constructor)|"
- "constructor (the implicit copy constructor)|"
- "constructor (the implicit move constructor)|"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)|"
- "inherited constructor|"
- "inherited constructor }0%1"
- " not viable: cannot implicitly convert argument "
- "%diff{of type $ to $|type to parameter type}2,3 for "
- "%select{%ordinal5 argument|object argument}4 under ARC">;
-def note_ovl_candidate_bad_lvalue : Note<"candidate "
- "%select{function|function|constructor|"
- "function |function |constructor |"
- "constructor (the implicit default constructor)|"
- "constructor (the implicit copy constructor)|"
- "constructor (the implicit move constructor)|"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)|"
- "inherited constructor|"
- "inherited constructor }0%1"
- " not viable: expects an l-value for "
- "%select{%ordinal3 argument|object argument}2">;
-def note_ovl_candidate_bad_addrspace : Note<"candidate "
- "%select{function|function|constructor|"
- "function |function |constructor |"
- "constructor (the implicit default constructor)|"
- "constructor (the implicit copy constructor)|"
- "constructor (the implicit move constructor)|"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)|"
- "inherited constructor|"
- "inherited constructor }0%1 not viable: "
- "%select{%ordinal6|'this'}5 argument (%2) is in "
- "address space %3, but parameter must be in address space %4">;
-def note_ovl_candidate_bad_gc : Note<"candidate "
- "%select{function|function|constructor|"
- "function |function |constructor |"
- "constructor (the implicit default constructor)|"
- "constructor (the implicit copy constructor)|"
- "constructor (the implicit move constructor)|"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)|"
- "inherited constructor|"
- "inherited constructor }0%1 not viable: "
- "%select{%ordinal6|'this'}5 argument (%2) has %select{no|__weak|__strong}3 "
- "ownership, but parameter has %select{no|__weak|__strong}4 ownership">;
-def note_ovl_candidate_bad_ownership : Note<"candidate "
- "%select{function|function|constructor|"
- "function |function |constructor |"
- "constructor (the implicit default constructor)|"
- "constructor (the implicit copy constructor)|"
- "constructor (the implicit move constructor)|"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)|"
- "inherited constructor|"
- "inherited constructor }0%1 not viable: "
- "%select{%ordinal6|'this'}5 argument (%2) has "
- "%select{no|__unsafe_unretained|__strong|__weak|__autoreleasing}3 ownership,"
+ "; remove &}7">;
+def note_ovl_candidate_bad_arc_conv : Note<
+ "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
+ "cannot implicitly convert argument "
+ "%diff{of type $ to $|type to parameter type}3,4 for "
+ "%select{%ordinal6 argument|object argument}5 under ARC">;
+def note_ovl_candidate_bad_lvalue : Note<
+ "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
+ "expects an l-value for "
+ "%select{%ordinal4 argument|object argument}3">;
+def note_ovl_candidate_bad_addrspace : Note<
+ "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
+ "%select{%ordinal7|'this'}6 argument (%3) is in "
+ "address space %4, but parameter must be in address space %5">;
+def note_ovl_candidate_bad_gc : Note<
+ "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
+ "%select{%ordinal7|'this'}6 argument (%3) has %select{no|__weak|__strong}4 "
+ "ownership, but parameter has %select{no|__weak|__strong}5 ownership">;
+def note_ovl_candidate_bad_ownership : Note<
+ "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
+ "%select{%ordinal7|'this'}6 argument (%3) has "
+ "%select{no|__unsafe_unretained|__strong|__weak|__autoreleasing}4 ownership,"
" but parameter has %select{no|__unsafe_unretained|__strong|__weak|"
- "__autoreleasing}4 ownership">;
-def note_ovl_candidate_bad_cvr_this : Note<"candidate "
- "%select{|function|||function|||||"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)||}0 not viable: "
- "'this' argument has type %2, but method is not marked "
+ "__autoreleasing}5 ownership">;
+def note_ovl_candidate_bad_cvr_this : Note<
+ "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
+ "'this' argument has type %3, but method is not marked "
"%select{const|restrict|const or restrict|volatile|const or volatile|"
- "volatile or restrict|const, volatile, or restrict}3">;
-def note_ovl_candidate_bad_cvr : Note<"candidate "
- "%select{function|function|constructor|"
- "function |function |constructor |"
- "constructor (the implicit default constructor)|"
- "constructor (the implicit copy constructor)|"
- "constructor (the implicit move constructor)|"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)|"
- "inherited constructor|"
- "inherited constructor }0%1 not viable: "
- "%ordinal4 argument (%2) would lose "
+ "volatile or restrict|const, volatile, or restrict}4">;
+def note_ovl_candidate_bad_cvr : Note<
+ "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
+ "%ordinal5 argument (%3) would lose "
"%select{const|restrict|const and restrict|volatile|const and volatile|"
- "volatile and restrict|const, volatile, and restrict}3 qualifier"
- "%select{||s||s|s|s}3">;
-def note_ovl_candidate_bad_unaligned : Note<"candidate "
- "%select{function|function|constructor|"
- "function |function |constructor |"
- "constructor (the implicit default constructor)|"
- "constructor (the implicit copy constructor)|"
- "constructor (the implicit move constructor)|"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)|"
- "inherited constructor|"
- "inherited constructor }0%1 not viable: "
- "%ordinal4 argument (%2) would lose __unaligned qualifier">;
-def note_ovl_candidate_bad_base_to_derived_conv : Note<"candidate "
- "%select{function|function|constructor|"
- "function |function |constructor |"
- "constructor (the implicit default constructor)|"
- "constructor (the implicit copy constructor)|"
- "constructor (the implicit move constructor)|"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)|"
- "inherited constructor|"
- "inherited constructor }0%1 not viable: "
- "cannot %select{convert from|convert from|bind}2 "
- "%select{base class pointer|superclass|base class object of type}2 %3 to "
- "%select{derived class pointer|subclass|derived class reference}2 %4 for "
- "%ordinal5 argument">;
+ "volatile and restrict|const, volatile, and restrict}4 qualifier"
+ "%select{||s||s|s|s}4">;
+def note_ovl_candidate_bad_unaligned : Note<
+ "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
+ "%ordinal5 argument (%3) would lose __unaligned qualifier">;
+def note_ovl_candidate_bad_base_to_derived_conv : Note<
+ "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
+ "cannot %select{convert from|convert from|bind}3 "
+ "%select{base class pointer|superclass|base class object of type}3 %4 to "
+ "%select{derived class pointer|subclass|derived class reference}3 %5 for "
+ "%ordinal6 argument">;
def note_ovl_candidate_bad_target : Note<
- "candidate %select{function|function|constructor|"
- "function|function|constructor|"
- "constructor (the implicit default constructor)|"
- "constructor (the implicit copy constructor)|"
- "constructor (the implicit move constructor)|"
- "function (the implicit copy assignment operator)|"
- "function (the implicit move assignment operator)|"
- "inherited constructor|"
- "inherited constructor}0 not viable: "
+ "candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
"call to "
- "%select{__device__|__global__|__host__|__host__ __device__|invalid}1 function from"
- " %select{__device__|__global__|__host__|__host__ __device__|invalid}2 function">;
+ "%select{__device__|__global__|__host__|__host__ __device__|invalid}3 function from"
+ " %select{__device__|__global__|__host__|__host__ __device__|invalid}4 function">;
def note_implicit_member_target_infer_collision : Note<
- "implicit %select{"
- "default constructor|"
- "copy constructor|"
- "move constructor|"
- "copy assignment operator|"
- "move assignment operator|"
- "destructor}0 inferred target collision: call to both "
+ "implicit %sub{select_special_member_kind}0 inferred target collision: call to both "
"%select{__device__|__global__|__host__|__host__ __device__}1 and "
"%select{__device__|__global__|__host__|__host__ __device__}2 members">;
@@ -3883,9 +3749,8 @@
"overload resolution selected %select{unavailable|deleted}0 operator '%1'%2">;
def err_ovl_deleted_special_oper : Error<
"object of type %0 cannot be %select{constructed|copied|moved|assigned|"
- "assigned|destroyed}1 because its %select{default constructor|"
- "copy constructor|move constructor|copy assignment operator|"
- "move assignment operator|destructor}1 is implicitly deleted">;
+ "assigned|destroyed}1 because its %sub{select_special_member_kind}1 "
+ "is implicitly deleted">;
def err_ovl_no_viable_subscript :
Error<"no viable overloaded operator[] for type %0">;
def err_ovl_no_oper :
@@ -7765,9 +7630,8 @@
"an explicitly-defaulted %select{copy|move}0 assignment operator may not "
"have 'const'%select{, 'constexpr'|}1 or 'volatile' qualifiers">;
def err_defaulted_special_member_volatile_param : Error<
- "the parameter for an explicitly-defaulted %select{<<ERROR>>|"
- "copy constructor|move constructor|copy assignment operator|"
- "move assignment operator|<<ERROR>>}0 may not be volatile">;
+ "the parameter for an explicitly-defaulted %sub{select_special_member_kind}0 "
+ "may not be volatile">;
def err_defaulted_special_member_move_const_param : Error<
"the parameter for an explicitly-defaulted move "
"%select{constructor|assignment operator}0 may not be const">;
@@ -7779,17 +7643,13 @@
"the parameter for an explicitly-defaulted copy assignment operator must be an "
"lvalue reference type">;
def err_incorrect_defaulted_exception_spec : Error<
- "exception specification of explicitly defaulted %select{default constructor|"
- "copy constructor|move constructor|copy assignment operator|move assignment "
- "operator|destructor}0 does not match the "
- "calculated one">;
+ "exception specification of explicitly defaulted "
+ "%sub{select_special_member_kind}0 does not match the calculated one">;
def err_incorrect_defaulted_constexpr : Error<
- "defaulted definition of %select{default constructor|copy constructor|"
- "move constructor|copy assignment operator|move assignment operator}0 "
+ "defaulted definition of %sub{select_special_member_kind}0 "
"is not constexpr">;
def err_out_of_line_default_deletes : Error<
- "defaulting this %select{default constructor|copy constructor|move "
- "constructor|copy assignment operator|move assignment operator|destructor}0 "
+ "defaulting this %sub{select_special_member_kind}0 "
"would delete it after its first declaration">;
def warn_vbase_moved_multiple_times : Warning<
"defaulted move assignment operator of %0 will move assign virtual base "
Index: include/clang/Basic/Diagnostic.td
===================================================================
--- include/clang/Basic/Diagnostic.td
+++ include/clang/Basic/Diagnostic.td
@@ -39,6 +39,15 @@
def SFINAE_Report : SFINAEResponse;
def SFINAE_AccessControl : SFINAEResponse;
+// Textual substitutions which may be performed on the text of diagnostics
+class TextSubstitution<string Text> {
+ string Substitution = Text;
+ // TODO: These are only here to allow substitutions to be declared inline with
+ // diagnostics
+ string Component = "";
+ string CategoryName = "";
+}
+
// Diagnostic Categories. These can be applied to groups or individual
// diagnostics to specify a category.
class DiagCategory<string Name> {
Index: docs/InternalsManual.rst
===================================================================
--- docs/InternalsManual.rst
+++ docs/InternalsManual.rst
@@ -319,6 +319,32 @@
repetitive diagnostics and/or have an idea for a useful formatter, please bring
it up on the cfe-dev mailing list.
+**"sub" format**
+
+Example:
+ Given the following record definition of type ``TextSubstitution``:
+
+ .. code-block:: text
+
+ def select_ovl_candidate : TextSubstitution<
+ "%select{function|constructor}0%select{| template| %2}1">;
+
+ which can be used as
+
+ .. code-block:: text
+
+ def note_ovl_candidate : Note<
+ "candidate %sub{select_ovl_candidate}3,2,1 not viable">;
+
+ and will act as if we wrote
+ ``"candidate %select{function|constructor}3%select{| template| %1}2 not viable"``.
+Description:
+ This format specifier is used to avoid repeating strings verbatim in multiple
+ diagnostics. The argument to ``%sub`` must name a ``TextSubstitution`` tblgen
+ record. The substitution must specify all arguments used by the substituted,
+ and modifier indexes in the substitution are re-numbered accordingly. The
+ substituted text must itself be a valid format string before substitution.
+
.. _internals-producing-diag:
Producing the Diagnostic
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits