Implements a declarative approach to documenting individual attributes in Clang via a Documentation tablegen class. Also updates the internals manual with information about how to use this new, required, documentation feature.
This patch adds some very, very sparse initial documentation for some attributes. Additional effort from attribute authors is greatly appreciated. llvm-svn: 201515
This commit is contained in:
parent
6774f2241d
commit
97dba048a3
|
|
@ -0,0 +1,6 @@
|
|||
..
|
||||
-------------------------------------------------------------------
|
||||
NOTE: This file is a placeholder that gets replaced by running
|
||||
clang-tblgen -gen-attr-docs. You should not edit this file by
|
||||
hand, nor should you commit changes to this file.
|
||||
-------------------------------------------------------------------
|
||||
|
|
@ -1727,6 +1727,21 @@ If additional functionality is desired for the semantic form of the attribute,
|
|||
the ``AdditionalMembers`` field specifies code to be copied verbatim into the
|
||||
semantic attribute class object.
|
||||
|
||||
All attributes must have one or more form of documentation, which is provided
|
||||
in the ``Documentation`` list. Generally, the documentation for an attribute
|
||||
is a stand-alone definition in `include/clang/Basic/AttrDocs.td
|
||||
<http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/AttdDocs.td?view=markup>`_
|
||||
that is named after the attribute being documented. Each documentation element
|
||||
is given a ``Category`` (variable, function, or type) and ``Content``. A single
|
||||
attribute may contain multiple documentation elements for distinct categories.
|
||||
For instance, an attribute which can appertain to both function and types (such
|
||||
as a calling convention attribute), should contain two documentation elements.
|
||||
The ``Content`` for an attribute uses reStructuredText (RST) syntax.
|
||||
|
||||
If an attribute is used internally by the compiler, but is not written by users
|
||||
(such as attributes with an empty spelling list), it can use the
|
||||
``Undocumented`` documentation element.
|
||||
|
||||
Boilerplate
|
||||
^^^^^^^^^^^
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
:maxdepth: 1
|
||||
|
||||
ReleaseNotes
|
||||
AttributeReference
|
||||
|
||||
Using Clang as a Compiler
|
||||
=========================
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,78 @@
|
|||
//==--- AttrDocs.td - Attribute 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-attr-docs. Do not edit this file by hand!!
|
||||
-------------------------------------------------------------------
|
||||
|
||||
===================
|
||||
Attributes in Clang
|
||||
===================
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
This page lists the attributes currently supported by Clang.
|
||||
}];
|
||||
}
|
||||
|
||||
def TLSModelDocs : Documentation {
|
||||
let Category = DocCatVariable;
|
||||
let Content = [{
|
||||
The ``tls_model`` attribute allows you to specify which thread-local storage
|
||||
model to use. It accepts the following strings:
|
||||
|
||||
* global-dynamic
|
||||
* local-dynamic
|
||||
* initial-exec
|
||||
* local-exec
|
||||
|
||||
TLS models are mutually exclusive.
|
||||
}];
|
||||
}
|
||||
|
||||
def CarriesDependencyDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Content = [{
|
||||
The ``carries_dependency`` attribute specifies dependency propagation into and
|
||||
out of functions.
|
||||
|
||||
When specified on a function or Objective-C method, the ``carries_depedency``
|
||||
attribute means that the return value carries a dependency out of the function,
|
||||
so that the implementation need not constrain ordering upon return from that
|
||||
function. Implementations of the function and its caller may choose to preserve
|
||||
dependencies instead of emitting memory ordering instructions such as fences.
|
||||
|
||||
Note, this attribute does not change the meaning of the program, but may result
|
||||
in generatation of more efficient code.
|
||||
}];
|
||||
}
|
||||
|
||||
def C11NoReturnDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Content = [{
|
||||
A function declared as ``_Noreturn`` shall not return to its caller. The
|
||||
compiler will generate a diagnostic for a function declared as ``_Noreturn``
|
||||
that appears to be capable of returning to its caller.
|
||||
}];
|
||||
}
|
||||
|
||||
def CXX11NoReturnDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Content = [{
|
||||
A function declared as ``[[noreturn]]`` shall not return to its caller. The
|
||||
compiler will generate a diagnostic for a function declared as ``[[noreturn]]``
|
||||
that appears to be capable of returning to its caller.
|
||||
}];
|
||||
}
|
||||
|
|
@ -2635,4 +2635,232 @@ void EmitClangAttrParserStringSwitches(RecordKeeper &Records,
|
|||
emitClangAttrLateParsedList(Records, OS);
|
||||
}
|
||||
|
||||
class DocumentationData {
|
||||
public:
|
||||
enum DocCategory {
|
||||
Function,
|
||||
Variable,
|
||||
Type,
|
||||
Undocumented
|
||||
};
|
||||
|
||||
DocCategory Category;
|
||||
const Record &Documentation;
|
||||
const Record &Attribute;
|
||||
|
||||
DocumentationData(DocCategory Category, const Record &Documentation,
|
||||
const Record &Attribute)
|
||||
: Category(Category), Documentation(Documentation), Attribute(Attribute) {
|
||||
}
|
||||
};
|
||||
|
||||
static void WriteCategoryHeader(DocumentationData::DocCategory Category,
|
||||
raw_ostream &OS) {
|
||||
OS << "\n";
|
||||
switch (Category) {
|
||||
case DocumentationData::Undocumented:
|
||||
assert(false && "Undocumented attributes are not documented!");
|
||||
break;
|
||||
case DocumentationData::Function:
|
||||
OS << "Function Attributes\n";
|
||||
OS << "===================\n";
|
||||
break;
|
||||
case DocumentationData::Variable:
|
||||
OS << "Variable Attributes\n";
|
||||
OS << "===================\n";
|
||||
break;
|
||||
case DocumentationData::Type:
|
||||
OS << "Type Attributes\n";
|
||||
OS << "===============\n";
|
||||
break;
|
||||
}
|
||||
OS << "\n";
|
||||
}
|
||||
|
||||
static void WriteDocumentation(const DocumentationData &Doc,
|
||||
raw_ostream &OS) {
|
||||
// FIXME: there is no way to have a per-spelling category for the attribute
|
||||
// documentation. This may not be a limiting factor since the spellings
|
||||
// should generally be consistently applied across the category.
|
||||
|
||||
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Doc.Attribute);
|
||||
|
||||
// Determine the heading to be used for this attribute.
|
||||
std::string Heading = Doc.Documentation.getValueAsString("Heading");
|
||||
if (Heading.empty()) {
|
||||
// If there's only one spelling, we can simply use that.
|
||||
if (Spellings.size() == 1)
|
||||
Heading = Spellings.begin()->name();
|
||||
else {
|
||||
std::set<std::string> Uniques;
|
||||
for (std::vector<FlattenedSpelling>::const_iterator I = Spellings.begin(),
|
||||
E = Spellings.end(); I != E && Uniques.size() <= 1; ++I) {
|
||||
std::string Spelling = NormalizeNameForSpellingComparison(I->name());
|
||||
Uniques.insert(Spelling);
|
||||
}
|
||||
// If the semantic map has only one spelling, that is sufficient for our
|
||||
// needs.
|
||||
if (Uniques.size() == 1)
|
||||
Heading = *Uniques.begin();
|
||||
}
|
||||
}
|
||||
|
||||
// If the heading is still empty, it is an error.
|
||||
if (Heading.empty())
|
||||
PrintFatalError(Doc.Attribute.getLoc(),
|
||||
"This attribute requires a heading to be specified");
|
||||
|
||||
// Gather a list of unique spellings; this is not the same as the semantic
|
||||
// spelling for the attribute. Variations in underscores and other non-
|
||||
// semantic characters are still acceptable.
|
||||
std::vector<std::string> Names;
|
||||
|
||||
enum SpellingKind {
|
||||
GNU = 1 << 0,
|
||||
CXX11 = 1 << 1,
|
||||
Declspec = 1 << 2,
|
||||
Keyword = 1 << 3
|
||||
};
|
||||
|
||||
unsigned SupportedSpellings = 0;
|
||||
for (std::vector<FlattenedSpelling>::const_iterator I = Spellings.begin(),
|
||||
E = Spellings.end(); I != E; ++I) {
|
||||
SpellingKind Kind = StringSwitch<SpellingKind>(I->variety())
|
||||
.Case("GNU", GNU)
|
||||
.Case("CXX11", CXX11)
|
||||
.Case("Declspec", Declspec)
|
||||
.Case("Keyword", Keyword);
|
||||
|
||||
// Mask in the supported spelling.
|
||||
SupportedSpellings |= Kind;
|
||||
|
||||
std::string Name;
|
||||
if (Kind == CXX11 && !I->nameSpace().empty())
|
||||
Name = I->nameSpace() + "::";
|
||||
Name += I->name();
|
||||
|
||||
// If this name is the same as the heading, do not add it.
|
||||
if (Name != Heading)
|
||||
Names.push_back(Name);
|
||||
}
|
||||
|
||||
// Print out the heading for the attribute. If there are alternate spellings,
|
||||
// then display those after the heading.
|
||||
if (!Names.empty()) {
|
||||
Heading += " (";
|
||||
for (std::vector<std::string>::const_iterator I = Names.begin(),
|
||||
E = Names.end(); I != E; ++I) {
|
||||
if (I != Names.begin())
|
||||
Heading += ", ";
|
||||
Heading += *I;
|
||||
}
|
||||
Heading += ")";
|
||||
}
|
||||
OS << Heading << "\n" << std::string(Heading.length(), '-') << "\n";
|
||||
|
||||
if (!SupportedSpellings)
|
||||
PrintFatalError(Doc.Attribute.getLoc(),
|
||||
"Attribute has no supported spellings; cannot be "
|
||||
"documented");
|
||||
|
||||
// List what spelling syntaxes the attribute supports.
|
||||
OS << ".. csv-table:: Supported Syntaxes\n";
|
||||
OS << " :header: \"GNU\", \"C++11\", \"__declspec\", \"Keyword\"\n\n";
|
||||
OS << " \"";
|
||||
if (SupportedSpellings & GNU) OS << "X";
|
||||
OS << "\",\"";
|
||||
if (SupportedSpellings & CXX11) OS << "X";
|
||||
OS << "\",\"";
|
||||
if (SupportedSpellings & Declspec) OS << "X";
|
||||
OS << "\",\"";
|
||||
if (SupportedSpellings & Keyword) OS << "X";
|
||||
OS << "\"\n\n";
|
||||
|
||||
// If the attribute is deprecated, print a message about it, and possibly
|
||||
// provide a replacement attribute.
|
||||
if (!Doc.Documentation.isValueUnset("Deprecated")) {
|
||||
OS << "This attribute has been deprecated, and may be removed in a future "
|
||||
<< "version of Clang.";
|
||||
const Record &Deprecated = *Doc.Documentation.getValueAsDef("Deprecated");
|
||||
std::string Replacement = Deprecated.getValueAsString("Replacement");
|
||||
if (!Replacement.empty())
|
||||
OS << " This attribute has been superseded by ``"
|
||||
<< Replacement << "``.";
|
||||
OS << "\n\n";
|
||||
}
|
||||
|
||||
std::string ContentStr = Doc.Documentation.getValueAsString("Content");
|
||||
// Trim leading and trailing newlines and spaces.
|
||||
StringRef Content(ContentStr);
|
||||
while (Content.startswith("\r") || Content.startswith("\n") ||
|
||||
Content.startswith(" ") || Content.startswith("\t"))
|
||||
Content = Content.substr(1);
|
||||
while (Content.endswith("\r") || Content.endswith("\n") ||
|
||||
Content.endswith(" ") || Content.endswith("\t"))
|
||||
Content = Content.substr(0, Content.size() - 1);
|
||||
OS << Content;
|
||||
|
||||
OS << "\n\n\n";
|
||||
}
|
||||
|
||||
void EmitClangAttrDocs(RecordKeeper &Records, raw_ostream &OS) {
|
||||
// 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");
|
||||
|
||||
typedef std::map<DocumentationData::DocCategory,
|
||||
std::vector<DocumentationData> > CategoryMap;
|
||||
CategoryMap SplitDocs;
|
||||
|
||||
// Gather the Documentation lists from each of the attributes, based on the
|
||||
// category provided.
|
||||
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
|
||||
for (std::vector<Record *>::const_iterator I = Attrs.begin(),
|
||||
E = Attrs.end(); I != E; ++I) {
|
||||
const Record &Attr = **I;
|
||||
std::vector<Record *> Docs = Attr.getValueAsListOfDefs("Documentation");
|
||||
for (std::vector<Record *>::const_iterator DI = Docs.begin(),
|
||||
DE = Docs.end(); DI != DE; ++DI) {
|
||||
const Record &Doc = **DI;
|
||||
DocumentationData::DocCategory Cat =
|
||||
StringSwitch<DocumentationData::DocCategory>(
|
||||
Doc.getValueAsDef("Category")->getValueAsString("Name"))
|
||||
.Case("Functions", DocumentationData::Function)
|
||||
.Case("Variables", DocumentationData::Variable)
|
||||
.Case("Types", DocumentationData::Type)
|
||||
.Case("Undocumented", DocumentationData::Undocumented);
|
||||
|
||||
// If the category is "undocumented", then there cannot be any other
|
||||
// documentation categories (otherwise, the attribute would become
|
||||
// documented).
|
||||
bool Undocumented = DocumentationData::Undocumented == Cat;
|
||||
if (Undocumented && Docs.size() > 1)
|
||||
PrintFatalError(Doc.getLoc(),
|
||||
"Attribute is \"Undocumented\", but has multiple "
|
||||
"documentation categories");
|
||||
|
||||
if (!Undocumented)
|
||||
SplitDocs[Cat].push_back(DocumentationData(Cat, Doc, Attr));
|
||||
}
|
||||
}
|
||||
|
||||
// Having split the attributes out based on what documentation goes where,
|
||||
// we can begin to generate sections of documentation.
|
||||
for (CategoryMap::const_iterator I = SplitDocs.begin(), E = SplitDocs.end();
|
||||
I != E; ++I) {
|
||||
WriteCategoryHeader(I->first, OS);
|
||||
|
||||
// Walk over each of the attributes in the category and write out their
|
||||
// documentation.
|
||||
for (auto D : I->second)
|
||||
WriteDocumentation(D, OS);
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace clang
|
||||
|
|
|
|||
|
|
@ -51,7 +51,8 @@ enum ActionType {
|
|||
GenClangCommentCommandList,
|
||||
GenArmNeon,
|
||||
GenArmNeonSema,
|
||||
GenArmNeonTest
|
||||
GenArmNeonTest,
|
||||
GenAttrDocs
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
|
@ -129,6 +130,8 @@ cl::opt<ActionType> Action(
|
|||
"Generate ARM NEON sema support for clang"),
|
||||
clEnumValN(GenArmNeonTest, "gen-arm-neon-test",
|
||||
"Generate ARM NEON tests for clang"),
|
||||
clEnumValN(GenAttrDocs, "gen-attr-docs",
|
||||
"Generate attribute documentation"),
|
||||
clEnumValEnd));
|
||||
|
||||
cl::opt<std::string>
|
||||
|
|
@ -226,6 +229,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
|
|||
case GenArmNeonTest:
|
||||
EmitNeonTest(Records, OS);
|
||||
break;
|
||||
case GenAttrDocs:
|
||||
EmitClangAttrDocs(Records, OS);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -62,4 +62,6 @@ void EmitNeon(RecordKeeper &Records, raw_ostream &OS);
|
|||
void EmitNeonSema(RecordKeeper &Records, raw_ostream &OS);
|
||||
void EmitNeonTest(RecordKeeper &Records, raw_ostream &OS);
|
||||
|
||||
void EmitClangAttrDocs(RecordKeeper &Records, raw_ostream &OS);
|
||||
|
||||
} // end namespace clang
|
||||
|
|
|
|||
Loading…
Reference in New Issue