Author: Aaron Ballman
Date: 2026-06-11T13:53:42-04:00
New Revision: 96eb0cb1941f6cb91404b28a08b39705fc1daf84

URL: 
https://github.com/llvm/llvm-project/commit/96eb0cb1941f6cb91404b28a08b39705fc1daf84
DIFF: 
https://github.com/llvm/llvm-project/commit/96eb0cb1941f6cb91404b28a08b39705fc1daf84.diff

LOG: Forcefully require new attributes to be documented (#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.

Added: 
    clang/test/AST/undocumented-attrs.cpp

Modified: 
    clang/utils/TableGen/ClangAttrEmitter.cpp
    clang/utils/TableGen/TableGen.cpp
    clang/utils/TableGen/TableGenBackends.h

Removed: 
    


################################################################################
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..fd6d411029e50 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -5568,6 +5568,49 @@ 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..cc02b08167894 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..87a6ddda2271d 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