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

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.

>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] 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);
 

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

Reply via email to