https://github.com/AaronBallman updated 
https://github.com/llvm/llvm-project/pull/203296

>From 322edd51977596e1fecc9d5f30bef15914da3d82 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <[email protected]>
Date: Thu, 11 Jun 2026 10:49:59 -0400
Subject: [PATCH 1/3] Forcefully require new attributes to be documented

Several years ago we began to require all new attributes be documented,
but we never had anything enforcing the requirement. However, despite
reviewers requesting this documentation, it's been missed often enough
that enforcement makes sense in order to reduce maintenance burden.

This adds a new tablegen option to spit out the list of undocumented
attributes, and a test which lists all of the existing undocumented
ones. If a new attribute is added, this test should catch the failure.
---
 clang/test/AST/undocumented-attrs.cpp     | 94 +++++++++++++++++++++++
 clang/utils/TableGen/ClangAttrEmitter.cpp | 44 +++++++++++
 clang/utils/TableGen/TableGen.cpp         |  7 ++
 clang/utils/TableGen/TableGenBackends.h   |  2 +
 4 files changed, 147 insertions(+)
 create mode 100644 clang/test/AST/undocumented-attrs.cpp

diff --git a/clang/test/AST/undocumented-attrs.cpp 
b/clang/test/AST/undocumented-attrs.cpp
new file mode 100644
index 0000000000000..c7a2c74631ea2
--- /dev/null
+++ b/clang/test/AST/undocumented-attrs.cpp
@@ -0,0 +1,94 @@
+// RUN: clang-tblgen -gen-clang-attr-undocumented-list -I%S/../../include/ 
%S/../../include/clang/Basic/Attr.td -o - | FileCheck %s
+
+This test serves to prevent us adding new attributes to Clang that are
+undocumented. All new attributes should be documented.
+
+The list of attributes below should NEVER grow. It should gradually shrink to 
0.
+
+CHECK-LABEL: Undocumented attributes:
+CHECK-NEXT:    AcquiredAfter
+CHECK-NEXT:    AcquiredBefore
+CHECK-NEXT:    Alias
+CHECK-NEXT:    Aligned
+CHECK-NEXT:    AnalyzerNoReturn
+CHECK-NEXT:    Annotate
+CHECK-NEXT:    ArcWeakrefUnavailable
+CHECK-NEXT:    AvailableOnlyInDefaultEvalMethod
+CHECK-NEXT:    Blocks
+CHECK-NEXT:    CDecl
+CHECK-NEXT:    CFAuditedTransfer
+CHECK-NEXT:    CFUnknownTransfer
+CHECK-NEXT:    CUDAConstant
+CHECK-NEXT:    CUDADevice
+CHECK-NEXT:    CUDAGlobal
+CHECK-NEXT:    CUDAHost
+CHECK-NEXT:    CUDALaunchBounds
+CHECK-NEXT:    CUDAShared
+CHECK-NEXT:    Capability
+CHECK-NEXT:    Common
+CHECK-NEXT:    Const
+CHECK-NEXT:    ConsumableAutoCast
+CHECK-NEXT:    ConsumableSetOnRead
+CHECK-NEXT:    FormatArg
+CHECK-NEXT:    GuardedBy
+CHECK-NEXT:    GuardedVar
+CHECK-NEXT:    IBAction
+CHECK-NEXT:    IBOutlet
+CHECK-NEXT:    IBOutletCollection
+CHECK-NEXT:    IntelOclBicc
+CHECK-NEXT:    LockReturned
+CHECK-NEXT:    Lockable
+CHECK-NEXT:    LocksExcluded
+CHECK-NEXT:    M68kInterrupt
+CHECK-NEXT:    MSP430Interrupt
+CHECK-NEXT:    MatrixType
+CHECK-NEXT:    MayAlias
+CHECK-NEXT:    Mips16
+CHECK-NEXT:    Mode
+CHECK-NEXT:    Naked
+CHECK-NEXT:    NeonPolyVectorType
+CHECK-NEXT:    NeonVectorType
+CHECK-NEXT:    NoCommon
+CHECK-NEXT:    NoFieldProtection
+CHECK-NEXT:    NoInstrumentFunction
+CHECK-NEXT:    NoMips16
+CHECK-NEXT:    NoReturn
+CHECK-NEXT:    NoThreadSafetyAnalysis
+CHECK-NEXT:    ObjCBridge
+CHECK-NEXT:    ObjCBridgeMutable
+CHECK-NEXT:    ObjCBridgeRelated
+CHECK-NEXT:    ObjCDesignatedInitializer
+CHECK-NEXT:    ObjCException
+CHECK-NEXT:    ObjCExplicitProtocolImpl
+CHECK-NEXT:    ObjCGC
+CHECK-NEXT:    ObjCIndependentClass
+CHECK-NEXT:    ObjCKindOf
+CHECK-NEXT:    ObjCNSObject
+CHECK-NEXT:    ObjCOwnership
+CHECK-NEXT:    ObjCPreciseLifetime
+CHECK-NEXT:    ObjCRequiresPropertyDefs
+CHECK-NEXT:    ObjCReturnsInnerPointer
+CHECK-NEXT:    ObjCRootClass
+CHECK-NEXT:    OverflowBehavior
+CHECK-NEXT:    Packed
+CHECK-NEXT:    Pascal
+CHECK-NEXT:    PointerFieldProtection
+CHECK-NEXT:    PtGuardedBy
+CHECK-NEXT:    PtGuardedVar
+CHECK-NEXT:    Pure
+CHECK-NEXT:    ReentrantCapability
+CHECK-NEXT:    ReqdWorkGroupSize
+CHECK-NEXT:    RequiresCapability
+CHECK-NEXT:    ReturnsTwice
+CHECK-NEXT:    ScopedLockable
+CHECK-NEXT:    Unavailable
+CHECK-NEXT:    Uuid
+CHECK-NEXT:    VTablePointerAuthentication
+CHECK-NEXT:    VecReturn
+CHECK-NEXT:    VecTypeHint
+CHECK-NEXT:    VectorSize
+CHECK-NEXT:    Visibility
+CHECK-NEXT:    WeakImport
+CHECK-NEXT:    WeakRef
+CHECK-NEXT:    WorkGroupSizeHint
+CHECK-NEXT: Total: 85
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp 
b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 1eaec5f07c75e..b230bfa7e3f01 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -5568,6 +5568,50 @@ static void WriteDocumentation(const RecordKeeper 
&Records,
   OS << "\n\n\n";
 }
 
+void GetListOfUndocumentedAttributes(
+    const RecordKeeper &Records,
+    std::vector<const Record *> &UndocumentedAttrs) {
+  const Record *Documentation = Records.getDef("GlobalDocumentation");
+  if (!Documentation) {
+    PrintFatalError("The Documentation top-level definition is missing.");
+    return;
+  }
+
+  for (const auto *A : Records.getAllDerivedDefinitions("Attr")) {
+    const Record &Attr = *A;
+    std::vector<const Record *> Docs =
+        Attr.getValueAsListOfDefs("Documentation");
+    for (const auto *D : Docs) {
+      const Record &Doc = *D;
+      const Record *Category = Doc.getValueAsDef("Category");
+      if (Category->getValueAsString("Name") == "Undocumented")
+        UndocumentedAttrs.push_back(A);
+    }
+  }
+}
+
+void EmitClangUndocumentedAttrlist(const llvm::RecordKeeper &Records,
+                                   llvm::raw_ostream &OS) {
+  // Emit a newline separated list of attributes whose Documentation is set to
+  // Undocumented.
+  std::vector<const Record *> UndocumentedAttrs;
+  GetListOfUndocumentedAttributes(Records, UndocumentedAttrs);
+
+  // Print a small header; this helps catch the situation where someone adds an
+  // attribute without documentation but it is alphabetically before the first
+  // attribute in the test file.
+  OS << "Undocumented attributes:\n";
+
+  for (const auto* A : UndocumentedAttrs) {
+    OS << A->getName() << "\n";
+  }
+
+  // Also print the count; this helps catch attributes after the last one in
+  // the test file.
+  OS << "Total: " << UndocumentedAttrs.size() << "\n";
+}
+
+
 void EmitClangAttrDocs(const RecordKeeper &Records, raw_ostream &OS) {
   // Get the documentation introduction paragraph.
   const Record *Documentation = Records.getDef("GlobalDocumentation");
diff --git a/clang/utils/TableGen/TableGen.cpp 
b/clang/utils/TableGen/TableGen.cpp
index 0ce9d8306ae16..9e1a55dcbc63b 100644
--- a/clang/utils/TableGen/TableGen.cpp
+++ b/clang/utils/TableGen/TableGen.cpp
@@ -46,6 +46,7 @@ enum ActionType {
   GenClangAttrIsTypeDependent,
   GenClangAttrTextNodeDump,
   GenClangAttrNodeTraverse,
+  GenClangAttrUndocumentedAttrList,
   GenClangBasicReader,
   GenClangBasicWriter,
   GenClangBuiltins,
@@ -191,6 +192,9 @@ cl::opt<ActionType> Action(
                    "Generate clang attribute text node dumper"),
         clEnumValN(GenClangAttrNodeTraverse, "gen-clang-attr-node-traverse",
                    "Generate clang attribute traverser"),
+        clEnumValN(GenClangAttrUndocumentedAttrList,
+                   "gen-clang-attr-undocumented-list",
+                   "Generate a list of undocumented attributes"),
         clEnumValN(GenClangBuiltins, "gen-clang-builtins",
                    "Generate clang builtins list"),
         clEnumValN(GenClangBuiltinTemplates, "gen-clang-builtin-templates",
@@ -450,6 +454,9 @@ bool ClangTableGenMain(raw_ostream &OS, const RecordKeeper 
&Records) {
   case GenClangAttrNodeTraverse:
     EmitClangAttrNodeTraverse(Records, OS);
     break;
+  case GenClangAttrUndocumentedAttrList:
+    EmitClangUndocumentedAttrlist(Records, OS);
+    break;
   case GenClangBuiltins:
     EmitClangBuiltins(Records, OS);
     break;
diff --git a/clang/utils/TableGen/TableGenBackends.h 
b/clang/utils/TableGen/TableGenBackends.h
index f9bd7ccf9d0d8..15d8b9894398f 100644
--- a/clang/utils/TableGen/TableGenBackends.h
+++ b/clang/utils/TableGen/TableGenBackends.h
@@ -88,6 +88,8 @@ void EmitClangAttrTextNodeDump(const llvm::RecordKeeper 
&Records,
                                llvm::raw_ostream &OS);
 void EmitClangAttrNodeTraverse(const llvm::RecordKeeper &Records,
                                llvm::raw_ostream &OS);
+void EmitClangUndocumentedAttrlist(const llvm::RecordKeeper &Records,
+                                   llvm::raw_ostream &OS);
 void EmitClangAttrDocTable(const llvm::RecordKeeper &Records,
                            llvm::raw_ostream &OS);
 

>From d2f61cbb3e83727ae0272e5bf9c6dcaa47e6cfb9 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <[email protected]>
Date: Thu, 11 Jun 2026 10:53:27 -0400
Subject: [PATCH 2/3] Update formatting; NFC

---
 clang/utils/TableGen/ClangAttrEmitter.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp 
b/clang/utils/TableGen/ClangAttrEmitter.cpp
index b230bfa7e3f01..3803ed49d6490 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -5602,7 +5602,7 @@ void EmitClangUndocumentedAttrlist(const 
llvm::RecordKeeper &Records,
   // attribute in the test file.
   OS << "Undocumented attributes:\n";
 
-  for (const auto* A : UndocumentedAttrs) {
+  for (const auto *A : UndocumentedAttrs) {
     OS << A->getName() << "\n";
   }
 

>From 1c2bca100eda6a8027fa4e5601a66368d0369886 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <[email protected]>
Date: Thu, 11 Jun 2026 11:11:14 -0400
Subject: [PATCH 3/3] clang-format again; NFC

---
 clang/utils/TableGen/ClangAttrEmitter.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp 
b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 3803ed49d6490..74de72ef41376 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -5611,7 +5611,6 @@ void EmitClangUndocumentedAttrlist(const 
llvm::RecordKeeper &Records,
   OS << "Total: " << UndocumentedAttrs.size() << "\n";
 }
 
-
 void EmitClangAttrDocs(const RecordKeeper &Records, raw_ostream &OS) {
   // Get the documentation introduction paragraph.
   const Record *Documentation = Records.getDef("GlobalDocumentation");

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to