[clang-tools-extra] [clang-tidy] Teach `modernize-deprecated-headers` about more no-op headers (PR #165674)

Victor Chernyakin via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 30 01:14:03 PDT 2025


https://github.com/localspook created https://github.com/llvm/llvm-project/pull/165674

First, this PR adds some more no-op C++ headers: `<cstdbool>`, `<cstdalign>`, and `<ciso646>`. Second, it adds some headers that are no-ops in C23: `<stdbool.h>`, `<stdnoreturn.h>`, and `<stdalign.h>`. Up until now, this was a C++-only check, so adding support for C headers required somewhat invasive changes. In particular, the current docs are written from a C++ point of view, so I had to rework them to make it clear that the check is relevant for C as well.

>From 030982462f365563135b881e2770da82222b039a Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Thu, 30 Oct 2025 01:08:37 -0700
Subject: [PATCH] [clang-tidy] Teach `modernize-deprecated-headers` about more
 no-op headers

---
 .../modernize/DeprecatedHeadersCheck.cpp      | 23 +++--
 .../modernize/DeprecatedHeadersCheck.h        |  2 +-
 clang-tools-extra/docs/ReleaseNotes.rst       |  4 +
 .../checks/modernize/deprecated-headers.rst   | 98 ++++++++++---------
 .../Inputs/deprecated-headers/ciso646         |  0
 .../Inputs/deprecated-headers/cstdalign       |  0
 .../Inputs/deprecated-headers/cstdbool        |  0
 .../Inputs/deprecated-headers/stdnoreturn.h   |  0
 .../modernize/deprecated-headers-c23.c        | 25 +++++
 .../modernize/deprecated-headers-cxx03.cpp    | 18 ++++
 .../modernize/deprecated-headers-cxx11.cpp    | 18 ++++
 11 files changed, 134 insertions(+), 54 deletions(-)
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/deprecated-headers/ciso646
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/deprecated-headers/cstdalign
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/deprecated-headers/cstdbool
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/deprecated-headers/stdnoreturn.h
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/deprecated-headers-c23.c

diff --git a/clang-tools-extra/clang-tidy/modernize/DeprecatedHeadersCheck.cpp b/clang-tools-extra/clang-tidy/modernize/DeprecatedHeadersCheck.cpp
index 1de9e136c5719..aa27cfd5ce81d 100644
--- a/clang-tools-extra/clang-tidy/modernize/DeprecatedHeadersCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/DeprecatedHeadersCheck.cpp
@@ -115,9 +115,9 @@ void DeprecatedHeadersCheck::check(
   // Emit all the remaining reports.
   for (const IncludeMarker &Marker : IncludesToBeProcessed) {
     if (Marker.Replacement.empty()) {
-      diag(Marker.DiagLoc,
-           "including '%0' has no effect in C++; consider removing it")
-          << Marker.FileName
+      diag(Marker.DiagLoc, "including '%0' has no effect %select{since C23|in "
+                           "C++}1; consider removing it")
+          << Marker.FileName << getLangOpts().CPlusPlus
           << FixItHint::CreateRemoval(Marker.ReplacementRange);
     } else {
       diag(Marker.DiagLoc, "inclusion of deprecated C++ header "
@@ -147,7 +147,9 @@ IncludeModernizePPCallbacks::IncludeModernizePPCallbacks(
       {"string.h", "cstring"}, {"time.h", "ctime"},
       {"wchar.h", "cwchar"},   {"wctype.h", "cwctype"},
   };
-  CStyledHeaderToCxx.insert(std::begin(CXX98Headers), std::end(CXX98Headers));
+
+  if (LangOpts.CPlusPlus)
+    CStyledHeaderToCxx.insert(std::begin(CXX98Headers), std::end(CXX98Headers));
 
   static constexpr std::pair<StringRef, StringRef> CXX11Headers[] = {
       {"fenv.h", "cfenv"},         {"stdint.h", "cstdint"},
@@ -157,9 +159,16 @@ IncludeModernizePPCallbacks::IncludeModernizePPCallbacks(
   if (LangOpts.CPlusPlus11)
     CStyledHeaderToCxx.insert(std::begin(CXX11Headers), std::end(CXX11Headers));
 
-  static constexpr StringRef HeadersToDelete[] = {"stdalign.h", "stdbool.h",
-                                                  "iso646.h"};
-  DeleteHeaders.insert_range(HeadersToDelete);
+  static constexpr StringRef CXXHeadersToDelete[] = {"stdalign.h", "cstdalign",
+                                                     "stdbool.h",  "cstdbool",
+                                                     "iso646.h",   "ciso646"};
+  if (LangOpts.CPlusPlus)
+    DeleteHeaders.insert_range(CXXHeadersToDelete);
+
+  static constexpr StringRef C23HeadersToDelete[] = {"stdalign.h", "stdbool.h",
+                                                     "stdnoreturn.h"};
+  if (LangOpts.C23)
+    DeleteHeaders.insert_range(C23HeadersToDelete);
 }
 
 void IncludeModernizePPCallbacks::InclusionDirective(
diff --git a/clang-tools-extra/clang-tidy/modernize/DeprecatedHeadersCheck.h b/clang-tools-extra/clang-tidy/modernize/DeprecatedHeadersCheck.h
index badb2b41f164f..1b7c1821fb19c 100644
--- a/clang-tools-extra/clang-tidy/modernize/DeprecatedHeadersCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/DeprecatedHeadersCheck.h
@@ -34,7 +34,7 @@ class DeprecatedHeadersCheck : public ClangTidyCheck {
 public:
   DeprecatedHeadersCheck(StringRef Name, ClangTidyContext *Context);
   bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
-    return LangOpts.CPlusPlus;
+    return LangOpts.CPlusPlus || LangOpts.C23;
   }
   void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
   void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 33cc401bcb78f..c2ed8b643c590 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -346,6 +346,10 @@ Changes in existing checks
   <clang-tidy/checks/modernize/avoid-c-arrays>` to not diagnose array types
   which are part of an implicit instantiation of a template.
 
+- Improved :doc:`modernize-deprecated-headers
+  <clang-tidy/checks/modernize/deprecated-headers>` to diagnose more
+  deprecated headers, in both C++ and (for the first time) in C.
+
 - Improved :doc:`modernize-use-constraints
   <clang-tidy/checks/modernize/use-constraints>` check by fixing a crash on
   uses of non-standard ``enable_if`` with a signature different from
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/deprecated-headers.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/deprecated-headers.rst
index 6c35cd4e53d87..8dcc8580b2ccc 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/deprecated-headers.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/deprecated-headers.rst
@@ -3,12 +3,55 @@
 modernize-deprecated-headers
 ============================
 
-Some headers from C library were deprecated in C++ and are no longer welcome in
-C++ codebases. Some have no effect in C++. For more details refer to the C++14
-Standard [depr.c.headers] section.
-
-This check replaces C standard library headers with their C++ alternatives and
-removes redundant ones.
+There exist headers that produce no effect when included, which are there
+solely to ease migrating code. The check will suggest removing them.
+In C++, they are:
+
+* ``stdalign.h`` / ``cstdalign``
+* ``stdbool.h`` / ``cstdbool``
+* ``iso646.h`` / ``ciso646``
+
+And in C they are:
+
+* ``stdalign.h`` // No-op since C23
+* ``stdbool.h`` // No-op since C23
+* ``stdnoreturn.h`` // No-op since C23
+
+In C++, there is additionally a number of headers intended for
+interoperability with C, which should not be used in pure C++ code.
+The check will suggest replacing them with their C++ counterparts
+(e.g. replacing ``<signal.h>`` with ``<csignal>``). These headers are:
+
+* ``<assert.h>``
+* ``<complex.h>``
+* ``<ctype.h>``
+* ``<errno.h>``
+* ``<fenv.h>``     // deprecated since C++11
+* ``<float.h>``
+* ``<inttypes.h>``
+* ``<limits.h>``
+* ``<locale.h>``
+* ``<math.h>``
+* ``<setjmp.h>``
+* ``<signal.h>``
+* ``<stdarg.h>``
+* ``<stddef.h>``
+* ``<stdint.h>``
+* ``<stdio.h>``
+* ``<stdlib.h>``
+* ``<string.h>``
+* ``<tgmath.h>``   // deprecated since C++11
+* ``<time.h>``
+* ``<uchar.h>``    // deprecated since C++11
+* ``<wchar.h>``
+* ``<wctype.h>``
+
+Important note: the C++ headers are not identical to their C counterparts.
+The C headers provide names in the global namespace (e.g. ``<stdio.h>``
+provides ``printf``), but the C++ headers might provide them only in the
+``std`` namespace (e.g. ``<cstdio>`` provides ``std::printf``, but not
+necessarily ``printf``). The check can break code that uses the unqualified
+names.
 
 .. code-block:: c++
 
@@ -21,46 +64,9 @@ removes redundant ones.
   #include <cassert>
   // No 'stdbool.h' here.
 
-Important note: the Standard doesn't guarantee that the C++ headers declare all
-the same functions in the global namespace. The check in its current form can
-break the code that uses library symbols from the global namespace.
-
-* `<assert.h>`
-* `<complex.h>`
-* `<ctype.h>`
-* `<errno.h>`
-* `<fenv.h>`     // deprecated since C++11
-* `<float.h>`
-* `<inttypes.h>`
-* `<limits.h>`
-* `<locale.h>`
-* `<math.h>`
-* `<setjmp.h>`
-* `<signal.h>`
-* `<stdarg.h>`
-* `<stddef.h>`
-* `<stdint.h>`
-* `<stdio.h>`
-* `<stdlib.h>`
-* `<string.h>`
-* `<tgmath.h>`   // deprecated since C++11
-* `<time.h>`
-* `<uchar.h>`    // deprecated since C++11
-* `<wchar.h>`
-* `<wctype.h>`
-
-If the specified standard is older than C++11 the check will only replace
-headers deprecated before C++11, otherwise -- every header that appeared in
-the previous list.
-
-These headers don't have effect in C++:
-
-* `<iso646.h>`
-* `<stdalign.h>`
-* `<stdbool.h>`
-
-The checker ignores `include` directives within `extern "C" { ... }` blocks,
-since a library might want to expose some API for C and C++ libraries.
+The check will ignore `include` directives within `extern "C" { ... }`
+blocks, under the assumption that such code is an API meant to compile as
+both C and C++:
 
 .. code-block:: c++
 
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/deprecated-headers/ciso646 b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/deprecated-headers/ciso646
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/deprecated-headers/cstdalign b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/deprecated-headers/cstdalign
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/deprecated-headers/cstdbool b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/deprecated-headers/cstdbool
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/deprecated-headers/stdnoreturn.h b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/deprecated-headers/stdnoreturn.h
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/deprecated-headers-c23.c b/clang-tools-extra/test/clang-tidy/checkers/modernize/deprecated-headers-c23.c
new file mode 100644
index 0000000000000..f81f9981abaa6
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/deprecated-headers-c23.c
@@ -0,0 +1,25 @@
+// RUN: %check_clang_tidy -std=c23-or-later %s modernize-deprecated-headers %t -- -extra-arg-before=-isystem%S/Inputs/deprecated-headers
+
+#include <stdalign.h> // <stdalign.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdalign.h' has no effect since C23; consider removing it
+// CHECK-FIXES: // <stdalign.h>
+#include <stdbool.h> // <stdbool.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdbool.h' has no effect since C23; consider removing it
+// CHECK-FIXES: // <stdbool.h>
+#include <stdnoreturn.h> // <stdnoreturn.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdnoreturn.h' has no effect since C23; consider removing it
+// CHECK-FIXES: // <stdnoreturn.h>
+
+#include <stdio.h> // OK, not deprecated
+
+#include "stdalign.h" // "stdalign.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdalign.h' has no effect since C23; consider removing it
+// CHECK-FIXES: // "stdalign.h"
+#include "stdbool.h" // "stdbool.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdbool.h' has no effect since C23; consider removing it
+// CHECK-FIXES: // "stdbool.h"
+#include "stdnoreturn.h" // "stdnoreturn.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdnoreturn.h' has no effect since C23; consider removing it
+// CHECK-FIXES: // "stdnoreturn.h"
+
+#include "stdio.h" // OK, not deprecated
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/deprecated-headers-cxx03.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/deprecated-headers-cxx03.cpp
index b02dfd1ce976f..01bb62e99f16f 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/deprecated-headers-cxx03.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/deprecated-headers-cxx03.cpp
@@ -59,12 +59,21 @@
 #include <stdalign.h> // <stdalign.h>
 // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdalign.h' has no effect in C++; consider removing it
 // CHECK-FIXES: // <stdalign.h>
+#include <cstdalign> // <cstdalign>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'cstdalign' has no effect in C++; consider removing it
+// CHECK-FIXES: // <cstdalign>
 #include <stdbool.h> // <stdbool.h>
 // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdbool.h' has no effect in C++; consider removing it
 // CHECK-FIXES: // <stdbool.h>
+#include <cstdbool> // <cstdbool>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'cstdbool' has no effect in C++; consider removing it
+// CHECK-FIXES: // <cstdbool>
 #include <iso646.h> // <iso646.h>
 // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'iso646.h' has no effect in C++; consider removing it
 // CHECK-FIXES: // <iso646.h>
+#include <ciso646> // <ciso646>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'ciso646' has no effect in C++; consider removing it
+// CHECK-FIXES: // <ciso646>
 
 // Headers deprecated since C++11: expect no diagnostics.
 #include <fenv.h>
@@ -133,12 +142,21 @@
 #include "stdalign.h" // "stdalign.h"
 // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdalign.h' has no effect in C++; consider removing it
 // CHECK-FIXES: // "stdalign.h"
+#include "cstdalign" // "cstdalign"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'cstdalign' has no effect in C++; consider removing it
+// CHECK-FIXES: // "cstdalign"
 #include "stdbool.h" // "stdbool.h"
 // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdbool.h' has no effect in C++; consider removing it
 // CHECK-FIXES: // "stdbool.h"
+#include "cstdbool" // "cstdbool"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'cstdbool' has no effect in C++; consider removing it
+// CHECK-FIXES: // "cstdbool"
 #include "iso646.h" // "iso646.h"
 // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'iso646.h' has no effect in C++; consider removing it
 // CHECK-FIXES: // "iso646.h"
+#include "ciso646" // "ciso646"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'ciso646' has no effect in C++; consider removing it
+// CHECK-FIXES: // "ciso646"
 
 // Headers deprecated since C++11; expect no diagnostics
 #include "fenv.h"
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/deprecated-headers-cxx11.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/deprecated-headers-cxx11.cpp
index 99ef506276a66..d4af364f0a844 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/deprecated-headers-cxx11.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/deprecated-headers-cxx11.cpp
@@ -74,12 +74,21 @@
 #include <stdalign.h> // <stdalign.h>
 // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdalign.h' has no effect in C++; consider removing it
 // CHECK-FIXES: // <stdalign.h>
+#include <cstdalign> // <cstdalign>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'cstdalign' has no effect in C++; consider removing it
+// CHECK-FIXES: // <cstdalign>
 #include <stdbool.h> // <stdbool.h>
 // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdbool.h' has no effect in C++; consider removing it
 // CHECK-FIXES: // <stdbool.h>
+#include <cstdbool> // <cstdbool>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'cstdbool' has no effect in C++; consider removing it
+// CHECK-FIXES: // <cstdbool>
 #include <iso646.h> // <iso646.h>
 // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'iso646.h' has no effect in C++; consider removing it
 // CHECK-FIXES: // <iso646.h>
+#include <ciso646> // <ciso646>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'ciso646' has no effect in C++; consider removing it
+// CHECK-FIXES: // <ciso646>
 
 #include "assert.h"
 // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'assert.h'; consider using 'cassert' instead
@@ -155,9 +164,18 @@
 #include "stdalign.h" // "stdalign.h"
 // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdalign.h' has no effect in C++; consider removing it
 // CHECK-FIXES: // "stdalign.h"
+#include "cstdalign" // "cstdalign"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'cstdalign' has no effect in C++; consider removing it
+// CHECK-FIXES: // "cstdalign"
 #include "stdbool.h" // "stdbool.h"
 // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdbool.h' has no effect in C++; consider removing it
 // CHECK-FIXES: // "stdbool.h"
+#include "cstdbool" // "cstdbool"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'cstdbool' has no effect in C++; consider removing it
+// CHECK-FIXES: // "cstdbool"
 #include "iso646.h" // "iso646.h"
 // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'iso646.h' has no effect in C++; consider removing it
 // CHECK-FIXES: // "iso646.h"
+#include "ciso646" // "ciso646"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'ciso646' has no effect in C++; consider removing it
+// CHECK-FIXES: // "ciso646"



More information about the cfe-commits mailing list