Improve attribute documentation to list which spellings are used in which syntaxes.
authorRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 30 Aug 2018 19:16:33 +0000 (19:16 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 30 Aug 2018 19:16:33 +0000 (19:16 +0000)
Summary:
Instead of listing all the spellings (including attribute namespaces) in
the section heading, only list the actual attribute names there, and
list the spellings in the supported syntaxes table.

This allows us to properly describe things like [[fallthrough]], for
which we allow a clang:: prefix in C++ but not in C, and AlwaysInline,
which has one spelling as a GNU attribute and a different spelling as a
keyword, without needing to repeat the syntax description in the
documentation text.

Sample rendering: https://pste.eu/p/T1ZV.html

Reviewers: aaron.ballman

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D51473

llvm-svn: 341097

clang/include/clang/Basic/AttrDocs.td
clang/utils/TableGen/ClangAttrEmitter.cpp

index 3798c8e..aaa1935 100644 (file)
@@ -38,6 +38,10 @@ Attributes in Clang
 .. contents::
    :local:
 
+.. |br| raw:: html
+
+  <br/>
+
 Introduction
 ============
 
@@ -51,7 +55,7 @@ def SectionDocs : Documentation {
 The ``section`` attribute allows you to specify a specific section a
 global variable or function should be in after translation.
   }];
-  let Heading = "section (gnu::section, __declspec(allocate))";
+  let Heading = "section, __declspec(allocate)";
 }
 
 def InitSegDocs : Documentation {
@@ -270,7 +274,7 @@ that appears to be capable of returning to its caller.
 
 def AssertCapabilityDocs : Documentation {
   let Category = DocCatFunction;
-  let Heading = "assert_capability (assert_shared_capability, clang::assert_capability, clang::assert_shared_capability)";
+  let Heading = "assert_capability, assert_shared_capability";
   let Content = [{
 Marks a function that dynamically tests whether a capability is held, and halts
 the program if it is not held.
@@ -279,7 +283,7 @@ the program if it is not held.
 
 def AcquireCapabilityDocs : Documentation {
   let Category = DocCatFunction;
-  let Heading = "acquire_capability (acquire_shared_capability, clang::acquire_capability, clang::acquire_shared_capability)";
+  let Heading = "acquire_capability, acquire_shared_capability";
   let Content = [{
 Marks a function as acquiring a capability.
   }];
@@ -287,7 +291,7 @@ Marks a function as acquiring a capability.
 
 def TryAcquireCapabilityDocs : Documentation {
   let Category = DocCatFunction;
-  let Heading = "try_acquire_capability (try_acquire_shared_capability, clang::try_acquire_capability, clang::try_acquire_shared_capability)";
+  let Heading = "try_acquire_capability, try_acquire_shared_capability";
   let Content = [{
 Marks a function that attempts to acquire a capability. This function may fail to
 actually acquire the capability; they accept a Boolean value determining
@@ -298,7 +302,7 @@ the capability means success (false).
 
 def ReleaseCapabilityDocs : Documentation {
   let Category = DocCatFunction;
-  let Heading = "release_capability (release_shared_capability, clang::release_capability, clang::release_shared_capability)";
+  let Heading = "release_capability, release_shared_capability";
   let Content = [{
 Marks a function as releasing a capability.
   }];
@@ -1261,7 +1265,7 @@ of silently falling back on dynamic initialization.
 
 def WarnMaybeUnusedDocs : Documentation {
   let Category = DocCatVariable;
-  let Heading = "maybe_unused, unused, gnu::unused";
+  let Heading = "maybe_unused, unused";
   let Content = [{
 When passing the ``-Wunused`` flag to Clang, entities that are unused by the
 program may be diagnosed. The ``[[maybe_unused]]`` (or
@@ -1287,7 +1291,7 @@ enumerator, a non-static data member, or a label.
 
 def WarnUnusedResultsDocs : Documentation {
   let Category = DocCatFunction;
-  let Heading = "nodiscard, warn_unused_result, clang::warn_unused_result, gnu::warn_unused_result";
+  let Heading = "nodiscard, warn_unused_result";
   let Content  = [{
 Clang supports the ability to diagnose when the results of a function call
 expression are discarded under suspicious circumstances. A diagnostic is
@@ -1312,7 +1316,7 @@ potentially-evaluated discarded-value expression that is not explicitly cast to
 
 def FallthroughDocs : Documentation {
   let Category = DocCatStmt;
-  let Heading = "fallthrough, clang::fallthrough";
+  let Heading = "fallthrough";
   let Content = [{
 The ``fallthrough`` (or ``clang::fallthrough``) attribute is used
 to annotate intentional fall-through
@@ -1460,7 +1464,7 @@ on the command line.
 
 def MipsLongCallStyleDocs : Documentation {
   let Category = DocCatFunction;
-  let Heading = "long_call (gnu::long_call, gnu::far)";
+  let Heading = "long_call, far";
   let Content = [{
 Clang supports the ``__attribute__((long_call))``, ``__attribute__((far))``,
 and ``__attribute__((near))`` attributes on MIPS targets. These attributes may
@@ -1481,7 +1485,7 @@ as ``-mlong-calls`` and ``-mno-long-calls``.
 
 def MipsShortCallStyleDocs : Documentation {
   let Category = DocCatFunction;
-  let Heading = "short_call (gnu::short_call, gnu::near)";
+  let Heading = "short_call, near";
   let Content = [{
 Clang supports the ``__attribute__((long_call))``, ``__attribute__((far))``,
 ``__attribute__((short__call))``, and ``__attribute__((near))`` attributes
@@ -1940,7 +1944,7 @@ def NoSanitizeAddressDocs : Documentation {
   let Category = DocCatFunction;
   // This function has multiple distinct spellings, and so it requires a custom
   // heading to be specified. The most common spelling is sufficient.
-  let Heading = "no_sanitize_address (no_address_safety_analysis, gnu::no_address_safety_analysis, gnu::no_sanitize_address)";
+  let Heading = "no_sanitize_address, no_address_safety_analysis";
   let Content = [{
 .. _langext-address_sanitizer:
 
@@ -2563,7 +2567,6 @@ for further details including limitations of the unroll hints.
 
 def OpenCLUnrollHintDocs : Documentation {
   let Category = DocCatStmt;
-  let Heading = "__attribute__((opencl_unroll_hint))";
   let Content = [{
 The opencl_unroll_hint attribute qualifier can be used to specify that a loop
 (for, while and do loops) can be unrolled. This attribute qualifier can be
@@ -2576,7 +2579,6 @@ s6.11.5 for details.
 
 def OpenCLIntelReqdSubGroupSizeDocs : Documentation {
   let Category = DocCatStmt;
-  let Heading = "__attribute__((intel_reqd_sub_group_size))";
   let Content = [{
 The optional attribute intel_reqd_sub_group_size can be used to indicate that
 the kernel must be compiled and executed with the specified subgroup size. When
@@ -3396,7 +3398,7 @@ See the RenderScript_ documentation for more information.
 
 def XRayDocs : Documentation {
   let Category = DocCatFunction;
-  let Heading = "xray_always_instrument (clang::xray_always_instrument), xray_never_instrument (clang::xray_never_instrument), xray_log_args (clang::xray_log_args)";
+  let Heading = "xray_always_instrument, xray_never_instrument, xray_log_args";
   let Content = [{
 ``__attribute__((xray_always_instrument))`` or ``[[clang::xray_always_instrument]]`` is used to mark member functions (in C++), methods (in Objective C), and free functions (in C, C++, and Objective C) to be instrumented with XRay. This will cause the function to always have space at the beginning and exit points to allow for runtime patching.
 
index 706cb4f..bf35caa 100644 (file)
@@ -3747,18 +3747,66 @@ void EmitClangAttrSubjectMatchRulesParserStringSwitches(RecordKeeper &Records,
   getPragmaAttributeSupport(Records).generateParsingHelpers(OS);
 }
 
+enum class SpellingKind {
+  GNU,
+  CXX11,
+  C2x,
+  Declspec,
+  Microsoft,
+  Keyword,
+  Pragma,
+};
+static const size_t NumSpellingKinds = (size_t)SpellingKind::Pragma + 1;
+
+class SpellingList {
+  std::vector<std::string> Spellings[NumSpellingKinds];
+
+public:
+  ArrayRef<std::string> operator[](SpellingKind K) const {
+    return Spellings[(size_t)K];
+  }
+
+  void add(const Record &Attr, FlattenedSpelling Spelling) {
+    SpellingKind Kind = StringSwitch<SpellingKind>(Spelling.variety())
+                            .Case("GNU", SpellingKind::GNU)
+                            .Case("CXX11", SpellingKind::CXX11)
+                            .Case("C2x", SpellingKind::C2x)
+                            .Case("Declspec", SpellingKind::Declspec)
+                            .Case("Microsoft", SpellingKind::Microsoft)
+                            .Case("Keyword", SpellingKind::Keyword)
+                            .Case("Pragma", SpellingKind::Pragma);
+    std::string Name;
+    if (!Spelling.nameSpace().empty()) {
+      switch (Kind) {
+      case SpellingKind::CXX11:
+      case SpellingKind::C2x:
+        Name = Spelling.nameSpace() + "::";
+        break;
+      case SpellingKind::Pragma:
+        Name = Spelling.nameSpace() + " ";
+        break;
+      default:
+        PrintFatalError(Attr.getLoc(), "Unexpected namespace in spelling");
+      }
+    }
+    Name += Spelling.name();
+
+    Spellings[(size_t)Kind].push_back(Name);
+  }
+};
+
 class DocumentationData {
 public:
   const Record *Documentation;
   const Record *Attribute;
   std::string Heading;
-  unsigned SupportedSpellings;
+  SpellingList SupportedSpellings;
 
   DocumentationData(const Record &Documentation, const Record &Attribute,
-                    const std::pair<std::string, unsigned> HeadingAndKinds)
+                    std::pair<std::string, SpellingList> HeadingAndSpellings)
       : Documentation(&Documentation), Attribute(&Attribute),
-        Heading(std::move(HeadingAndKinds.first)),
-        SupportedSpellings(HeadingAndKinds.second) {}
+        Heading(std::move(HeadingAndSpellings.first)),
+        SupportedSpellings(std::move(HeadingAndSpellings.second)) {}
 };
 
 static void WriteCategoryHeader(const Record *DocCategory,
@@ -3774,28 +3822,21 @@ static void WriteCategoryHeader(const Record *DocCategory,
   OS << "\n\n";
 }
 
-enum SpellingKind {
-  GNU = 1 << 0,
-  CXX11 = 1 << 1,
-  C2x = 1 << 2,
-  Declspec = 1 << 3,
-  Microsoft = 1 << 4,
-  Keyword = 1 << 5,
-  Pragma = 1 << 6
-};
-
-static std::pair<std::string, unsigned>
-GetAttributeHeadingAndSpellingKinds(const Record &Documentation,
-                                    const Record &Attribute) {
+static std::pair<std::string, SpellingList>
+GetAttributeHeadingAndSpellings(const Record &Documentation,
+                                const Record &Attribute) {
   // 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(Attribute);
+  if (Spellings.empty())
+    PrintFatalError(Attribute.getLoc(),
+                    "Attribute has no supported spellings; cannot be "
+                    "documented");
 
   // Determine the heading to be used for this attribute.
   std::string Heading = Documentation.getValueAsString("Heading");
-  bool CustomHeading = !Heading.empty();
   if (Heading.empty()) {
     // If there's only one spelling, we can simply use that.
     if (Spellings.size() == 1)
@@ -3819,51 +3860,11 @@ GetAttributeHeadingAndSpellingKinds(const Record &Documentation,
     PrintFatalError(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;
+  SpellingList SupportedSpellings;
+  for (const auto &I : Spellings)
+    SupportedSpellings.add(Attribute, I);
 
-  unsigned SupportedSpellings = 0;
-  for (const auto &I : Spellings) {
-    SpellingKind Kind = StringSwitch<SpellingKind>(I.variety())
-                            .Case("GNU", GNU)
-                            .Case("CXX11", CXX11)
-                            .Case("C2x", C2x)
-                            .Case("Declspec", Declspec)
-                            .Case("Microsoft", Microsoft)
-                            .Case("Keyword", Keyword)
-                            .Case("Pragma", Pragma);
-
-    // Mask in the supported spelling.
-    SupportedSpellings |= Kind;
-
-    std::string Name;
-    if ((Kind == CXX11 || Kind == C2x) && !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 (!CustomHeading && !Names.empty()) {
-    Heading += " (";
-    for (auto I = Names.begin(), E = Names.end(); I != E; ++I) {
-      if (I != Names.begin())
-        Heading += ", ";
-      Heading += *I;
-    }
-    Heading += ")";
-  }
-  if (!SupportedSpellings)
-    PrintFatalError(Attribute.getLoc(),
-                    "Attribute has no supported spellings; cannot be "
-                    "documented");
-  return std::make_pair(std::move(Heading), SupportedSpellings);
+  return std::make_pair(std::move(Heading), std::move(SupportedSpellings));
 }
 
 static void WriteDocumentation(RecordKeeper &Records,
@@ -3872,23 +3873,29 @@ static void WriteDocumentation(RecordKeeper &Records,
 
   // List what spelling syntaxes the attribute supports.
   OS << ".. csv-table:: Supported Syntaxes\n";
-  OS << "   :header: \"GNU\", \"C++11\", \"C2x\", \"__declspec\", \"Keyword\",";
-  OS << " \"Pragma\", \"Pragma clang attribute\"\n\n";
+  OS << "   :header: \"GNU\", \"C++11\", \"C2x\", \"``__declspec``\",";
+  OS << " \"Keyword\", \"``#pragma``\", \"``#pragma clang attribute``\"\n\n";
   OS << "   \"";
-  if (Doc.SupportedSpellings & GNU) OS << "X";
-  OS << "\",\"";
-  if (Doc.SupportedSpellings & CXX11) OS << "X";
-  OS << "\",\"";
-  if (Doc.SupportedSpellings & C2x) OS << "X";
-  OS << "\",\"";
-  if (Doc.SupportedSpellings & Declspec) OS << "X";
-  OS << "\",\"";
-  if (Doc.SupportedSpellings & Keyword) OS << "X";
-  OS << "\", \"";
-  if (Doc.SupportedSpellings & Pragma) OS << "X";
-  OS << "\", \"";
-  if (getPragmaAttributeSupport(Records).isAttributedSupported(*Doc.Attribute))
-    OS << "X";
+  for (size_t Kind = 0; Kind != NumSpellingKinds; ++Kind) {
+    SpellingKind K = (SpellingKind)Kind;
+    // FIXME: Why are Microsoft spellings not listed?
+    if (K == SpellingKind::Microsoft)
+      continue;
+
+    bool PrintedAny = false;
+    for (StringRef Spelling : Doc.SupportedSpellings[K]) {
+      if (PrintedAny)
+        OS << " |br| ";
+      OS << "``" << Spelling << "``";
+      PrintedAny = true;
+    }
+
+    OS << "\",\"";
+  }
+
+  if (getPragmaAttributeSupport(Records).isAttributedSupported(
+          *Doc.Attribute))
+    OS << "Yes";
   OS << "\"\n\n";
 
   // If the attribute is deprecated, print a message about it, and possibly
@@ -3944,7 +3951,7 @@ void EmitClangAttrDocs(RecordKeeper &Records, raw_ostream &OS) {
 
       if (!Undocumented)
         SplitDocs[Category].push_back(DocumentationData(
-            Doc, Attr, GetAttributeHeadingAndSpellingKinds(Doc, Attr)));
+            Doc, Attr, GetAttributeHeadingAndSpellings(Doc, Attr)));
     }
   }