summaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
authorAaron Ballman <aaron@aaronballman.com>2014-02-17 15:27:10 +0000
committerAaron Ballman <aaron@aaronballman.com>2014-02-17 15:27:10 +0000
commitb0d5955832088e3771d541dda30cc8f73e5d93f2 (patch)
tree8b04bf5fcf95a17852cc3e003ca3b5e6facf41ba /utils
parent7e2fb94d6283456401d5154b9fb3d1c47b889b4a (diff)
downloadclang-b0d5955832088e3771d541dda30cc8f73e5d93f2.tar.gz
clang-b0d5955832088e3771d541dda30cc8f73e5d93f2.tar.bz2
clang-b0d5955832088e3771d541dda30cc8f73e5d93f2.tar.xz
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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@201515 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'utils')
-rw-r--r--utils/TableGen/ClangAttrEmitter.cpp228
-rw-r--r--utils/TableGen/TableGen.cpp8
-rw-r--r--utils/TableGen/TableGenBackends.h2
3 files changed, 237 insertions, 1 deletions
diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp
index cd812cb1e9..f2bfa79def 100644
--- a/utils/TableGen/ClangAttrEmitter.cpp
+++ b/utils/TableGen/ClangAttrEmitter.cpp
@@ -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
diff --git a/utils/TableGen/TableGen.cpp b/utils/TableGen/TableGen.cpp
index e4e746c07d..6737f77749 100644
--- a/utils/TableGen/TableGen.cpp
+++ b/utils/TableGen/TableGen.cpp
@@ -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;
diff --git a/utils/TableGen/TableGenBackends.h b/utils/TableGen/TableGenBackends.h
index b936833547..f8e11a66eb 100644
--- a/utils/TableGen/TableGenBackends.h
+++ b/utils/TableGen/TableGenBackends.h
@@ -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