forked from OSchip/llvm-project
Add a mode to clang-tblgen to generate reference documentation for warning and
remark flags. For now I'm checking in a copy of the built documentation, but we can replace this with a placeholder (as we do for the attributes reference documentation) once we enable building this server-side. llvm-svn: 281192
This commit is contained in:
parent
4f730dc750
commit
b6a3b4ba61
File diff suppressed because it is too large
Load Diff
|
@ -107,6 +107,8 @@ Options to Control Error and Warning Messages
|
|||
.. option:: -Wfoo
|
||||
|
||||
Enable warning "foo".
|
||||
See the :doc:`diagnostics reference <DiagnosticsReference>` for a complete
|
||||
list of the warning flags that can be specified in this way.
|
||||
|
||||
.. option:: -Wno-foo
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ Using Clang as a Compiler
|
|||
UsersManual
|
||||
LanguageExtensions
|
||||
AttributeReference
|
||||
DiagnosticsReference
|
||||
CrossCompilation
|
||||
ThreadSafetyAnalysis
|
||||
AddressSanitizer
|
||||
|
|
|
@ -48,10 +48,13 @@ 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>; }
|
||||
|
||||
// This defines documentation for diagnostic groups.
|
||||
include "DiagnosticDocs.td"
|
||||
|
||||
// This defines all of the named diagnostic categories.
|
||||
include "DiagnosticCategories.td"
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
//==--- DiagnosticDocs.td - Diagnostic documentation ---------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
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;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
table.docutils tr + tr {
|
||||
border-top: 0.2em solid #aaa;
|
||||
}
|
||||
.error {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
color: #c70;
|
||||
}
|
||||
.warning {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
color: #c70;
|
||||
}
|
||||
.remark {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
color: #00c;
|
||||
}
|
||||
.diagtext {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
.. FIXME: Format this as .diagtext. rST appears to not support this. :(
|
||||
.. |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
|
||||
================
|
||||
}];
|
||||
}
|
||||
|
|
@ -427,7 +427,7 @@ def err_synthesized_property_name : Error<
|
|||
"expected a property name in @synthesize">;
|
||||
def warn_semicolon_before_method_body : Warning<
|
||||
"semicolon before method body is ignored">,
|
||||
InGroup<DiagGroup<"semicolon-before-method-body">>, DefaultIgnore;
|
||||
InGroup<SemiBeforeMethodBody>, DefaultIgnore;
|
||||
def note_extra_comma_message_arg : Note<
|
||||
"comma separating Objective-C messaging arguments">;
|
||||
|
||||
|
|
|
@ -5464,7 +5464,7 @@ def err_typecheck_invalid_lvalue_addrof : Error<
|
|||
"cannot take the address of an rvalue of type %0">;
|
||||
def ext_typecheck_addrof_temporary : ExtWarn<
|
||||
"taking the address of a temporary object of type %0">,
|
||||
InGroup<DiagGroup<"address-of-temporary">>, DefaultError;
|
||||
InGroup<AddressOfTemporary>, DefaultError;
|
||||
def err_typecheck_addrof_temporary : Error<
|
||||
"taking the address of a temporary object of type %0">;
|
||||
def err_typecheck_addrof_dtor : Error<
|
||||
|
@ -6821,7 +6821,7 @@ def warn_side_effects_typeid : Warning<
|
|||
"operand to 'typeid'">, InGroup<PotentiallyEvaluatedExpression>;
|
||||
def warn_unused_result : Warning<
|
||||
"ignoring return value of function declared with %0 attribute">,
|
||||
InGroup<DiagGroup<"unused-result">>;
|
||||
InGroup<UnusedResult>;
|
||||
def warn_unused_volatile : Warning<
|
||||
"expression result unused; assign into a variable to force a volatile load">,
|
||||
InGroup<DiagGroup<"unused-volatile-lvalue">>;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#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/TableGen/Error.h"
|
||||
#include "llvm/TableGen/Record.h"
|
||||
|
@ -27,6 +28,7 @@
|
|||
#include <cctype>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <set>
|
||||
using namespace llvm;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -141,6 +143,11 @@ static bool beforeThanCompare(const Record *LHS, const Record *RHS) {
|
|||
LHS->getLoc().front().getPointer() < RHS->getLoc().front().getPointer();
|
||||
}
|
||||
|
||||
static bool diagGroupBeforeByName(const Record *LHS, const Record *RHS) {
|
||||
return LHS->getValueAsString("GroupName") <
|
||||
RHS->getValueAsString("GroupName");
|
||||
}
|
||||
|
||||
static bool beforeThanCompareGroups(const GroupInfo *LHS, const GroupInfo *RHS){
|
||||
assert(!LHS->DiagsInGroup.empty() && !RHS->DiagsInGroup.empty());
|
||||
return beforeThanCompare(LHS->DiagsInGroup.front(),
|
||||
|
@ -892,4 +899,421 @@ void EmitClangDiagsIndexName(RecordKeeper &Records, raw_ostream &OS) {
|
|||
OS << "DIAG_NAME_INDEX(" << R.Name << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Diagnostic documentation generation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace docs {
|
||||
namespace {
|
||||
|
||||
/// Diagnostic text, parsed into pieces.
|
||||
struct DiagText {
|
||||
struct Piece {
|
||||
virtual void print(std::vector<std::string> &RST) = 0;
|
||||
};
|
||||
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 {
|
||||
std::vector<DiagText> Options;
|
||||
void print(std::vector<std::string> &RST) override;
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<Piece>> Pieces;
|
||||
|
||||
DiagText() {}
|
||||
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(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;
|
||||
|
||||
std::function<void(StringRef)> Visit = [&](StringRef GroupName) {
|
||||
auto &GroupInfo = DiagsInGroup.find(GroupName)->second;
|
||||
for (const Record *Diag : GroupInfo.DiagsInGroup)
|
||||
(isRemark(*Diag) ? AnyRemarks : AnyNonRemarks) = true;
|
||||
for (const auto &Name : GroupInfo.SubGroups)
|
||||
Visit(Name);
|
||||
};
|
||||
Visit(DiagGroup->getValueAsString("GroupName"));
|
||||
|
||||
if (AnyRemarks && AnyNonRemarks)
|
||||
PrintFatalError(
|
||||
DiagGroup->getLoc(),
|
||||
"Diagnostic group contains both remark and non-remark diagnostics");
|
||||
return AnyRemarks;
|
||||
}
|
||||
|
||||
std::string getDefaultSeverity(const Record *Diag) {
|
||||
return Diag->getValueAsDef("DefaultSeverity")->getValueAsString("Name");
|
||||
}
|
||||
|
||||
std::set<std::string>
|
||||
getDefaultSeverities(const Record *DiagGroup,
|
||||
const std::map<std::string, GroupInfo> &DiagsInGroup) {
|
||||
std::set<std::string> States;
|
||||
|
||||
std::function<void(StringRef)> Visit = [&](StringRef GroupName) {
|
||||
auto &GroupInfo = DiagsInGroup.find(GroupName)->second;
|
||||
for (const Record *Diag : GroupInfo.DiagsInGroup)
|
||||
States.insert(getDefaultSeverity(Diag));
|
||||
for (const auto &Name : GroupInfo.SubGroups)
|
||||
Visit(Name);
|
||||
};
|
||||
Visit(DiagGroup->getValueAsString("GroupName"));
|
||||
return States;
|
||||
}
|
||||
|
||||
void writeHeader(StringRef Str, raw_ostream &OS, char Kind = '-') {
|
||||
OS << Str << "\n" << std::string(Str.size(), Kind) << "\n";
|
||||
}
|
||||
|
||||
void writeDiagnosticText(StringRef Role, StringRef Text, raw_ostream &OS) {
|
||||
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);
|
||||
for (auto &Line : Out)
|
||||
OS << Line << "\n";
|
||||
OS << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace docs
|
||||
|
||||
void EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS) {
|
||||
using namespace docs;
|
||||
|
||||
// Get the documentation introduction paragraph.
|
||||
const Record *Documentation = Records.getDef("GlobalDocumentation");
|
||||
if (!Documentation) {
|
||||
PrintFatalError("The Documentation top-level definition is missing, "
|
||||
"no documentation will be generated.");
|
||||
return;
|
||||
}
|
||||
|
||||
OS << Documentation->getValueAsString("Intro") << "\n";
|
||||
|
||||
std::vector<Record*> Diags =
|
||||
Records.getAllDerivedDefinitions("Diagnostic");
|
||||
std::vector<Record*> DiagGroups =
|
||||
Records.getAllDerivedDefinitions("DiagGroup");
|
||||
std::sort(DiagGroups.begin(), DiagGroups.end(), diagGroupBeforeByName);
|
||||
|
||||
DiagGroupParentMap DGParentMap(Records);
|
||||
|
||||
std::map<std::string, GroupInfo> DiagsInGroup;
|
||||
groupDiagnostics(Diags, DiagGroups, DiagsInGroup);
|
||||
|
||||
// Compute the set of diagnostics that are in -Wpedantic.
|
||||
{
|
||||
RecordSet DiagsInPedantic;
|
||||
RecordSet GroupsInPedantic;
|
||||
InferPedantic inferPedantic(DGParentMap, Diags, DiagGroups, DiagsInGroup);
|
||||
inferPedantic.compute(&DiagsInPedantic, &GroupsInPedantic);
|
||||
auto &PedDiags = DiagsInGroup["pedantic"];
|
||||
PedDiags.DiagsInGroup.insert(PedDiags.DiagsInGroup.end(),
|
||||
DiagsInPedantic.begin(),
|
||||
DiagsInPedantic.end());
|
||||
for (auto *Group : GroupsInPedantic)
|
||||
PedDiags.SubGroups.push_back(Group->getValueAsString("GroupName"));
|
||||
}
|
||||
|
||||
// FIXME: Write diagnostic categories and link to diagnostic groups in each.
|
||||
|
||||
// Write out the diagnostic groups.
|
||||
for (const Record *G : DiagGroups) {
|
||||
bool IsRemarkGroup = isRemarkGroup(G, DiagsInGroup);
|
||||
auto &GroupInfo = DiagsInGroup[G->getValueAsString("GroupName")];
|
||||
bool IsSynonym = GroupInfo.DiagsInGroup.empty() &&
|
||||
GroupInfo.SubGroups.size() == 1;
|
||||
|
||||
writeHeader((IsRemarkGroup ? "-R" : "-W") +
|
||||
G->getValueAsString("GroupName"),
|
||||
OS);
|
||||
|
||||
if (!IsSynonym) {
|
||||
// FIXME: Ideally, all the diagnostics in a group should have the same
|
||||
// default state, but that is not currently the case.
|
||||
auto DefaultSeverities = getDefaultSeverities(G, DiagsInGroup);
|
||||
if (!DefaultSeverities.empty() && !DefaultSeverities.count("Ignored")) {
|
||||
bool AnyNonErrors = DefaultSeverities.count("Warning") ||
|
||||
DefaultSeverities.count("Remark");
|
||||
if (!AnyNonErrors)
|
||||
OS << "This diagnostic is an error by default, but the flag ``-Wno-"
|
||||
<< G->getValueAsString("GroupName") << "`` can be used to disable "
|
||||
<< "the error.\n\n";
|
||||
else
|
||||
OS << "This diagnostic is enabled by default.\n\n";
|
||||
} else if (DefaultSeverities.size() > 1) {
|
||||
OS << "Some of the diagnostics controlled by this flag are enabled "
|
||||
<< "by default.\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!GroupInfo.SubGroups.empty()) {
|
||||
if (IsSynonym)
|
||||
OS << "Synonym for ";
|
||||
else if (GroupInfo.DiagsInGroup.empty())
|
||||
OS << "Controls ";
|
||||
else
|
||||
OS << "Also controls ";
|
||||
|
||||
bool First = true;
|
||||
for (const auto &Name : GroupInfo.SubGroups) {
|
||||
if (!First) OS << ", ";
|
||||
OS << "`" << (IsRemarkGroup ? "-R" : "-W") << Name << "`_";
|
||||
First = false;
|
||||
}
|
||||
OS << ".\n\n";
|
||||
}
|
||||
|
||||
if (!GroupInfo.DiagsInGroup.empty()) {
|
||||
OS << "**Diagnostic text:**\n\n";
|
||||
for (const Record *D : GroupInfo.DiagsInGroup) {
|
||||
auto Severity = getDefaultSeverity(D);
|
||||
Severity[0] = tolower(Severity[0]);
|
||||
if (Severity == "ignored")
|
||||
Severity = IsRemarkGroup ? "remark" : "warning";
|
||||
writeDiagnosticText(Severity, D->getValueAsString("Text"), OS);
|
||||
}
|
||||
}
|
||||
|
||||
auto Doc = G->getValueAsString("Documentation");
|
||||
if (!Doc.empty())
|
||||
OS << Doc;
|
||||
else if (GroupInfo.SubGroups.empty() && GroupInfo.DiagsInGroup.empty())
|
||||
OS << "This diagnostic flag exists for GCC compatibility, and has no "
|
||||
"effect in Clang.\n";
|
||||
OS << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace clang
|
||||
|
|
|
@ -52,7 +52,8 @@ enum ActionType {
|
|||
GenArmNeon,
|
||||
GenArmNeonSema,
|
||||
GenArmNeonTest,
|
||||
GenAttrDocs
|
||||
GenAttrDocs,
|
||||
GenDiagDocs
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
@ -133,6 +134,8 @@ cl::opt<ActionType> Action(
|
|||
"Generate ARM NEON tests for clang"),
|
||||
clEnumValN(GenAttrDocs, "gen-attr-docs",
|
||||
"Generate attribute documentation"),
|
||||
clEnumValN(GenDiagDocs, "gen-diag-docs",
|
||||
"Generate attribute documentation"),
|
||||
clEnumValEnd));
|
||||
|
||||
cl::opt<std::string>
|
||||
|
@ -233,6 +236,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
|
|||
case GenAttrDocs:
|
||||
EmitClangAttrDocs(Records, OS);
|
||||
break;
|
||||
case GenDiagDocs:
|
||||
EmitClangDiagDocs(Records, OS);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -69,6 +69,7 @@ void EmitNeonSema2(RecordKeeper &Records, raw_ostream &OS);
|
|||
void EmitNeonTest2(RecordKeeper &Records, raw_ostream &OS);
|
||||
|
||||
void EmitClangAttrDocs(RecordKeeper &Records, raw_ostream &OS);
|
||||
void EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS);
|
||||
|
||||
} // end namespace clang
|
||||
|
||||
|
|
Loading…
Reference in New Issue