Author: Victor Chernyakin
Date: 2025-07-13T19:23:27+03:00
New Revision: 59b39c0031eded7c46e554b161382187cb2d0ca5

URL: 
https://github.com/llvm/llvm-project/commit/59b39c0031eded7c46e554b161382187cb2d0ca5
DIFF: 
https://github.com/llvm/llvm-project/commit/59b39c0031eded7c46e554b161382187cb2d0ca5.diff

LOG: [clang-tidy] Add new check: 
`readability-use-concise-preprocessor-directives` (#146830)

Closes #132561.

This is a check that rewrites `#if`s and `#elif`s like so:

```cpp
#if  defined(MEOW) // -> #ifdef  MEOW
#if !defined(MEOW) // -> #ifndef MEOW
```

And, since C23 and C++23:

```cpp
#elif  defined(MEOW) // -> #elifdef  MEOW
#elif !defined(MEOW) // -> #elifndef MEOW
```

Added: 
    
clang-tools-extra/clang-tidy/readability/UseConcisePreprocessorDirectivesCheck.cpp
    
clang-tools-extra/clang-tidy/readability/UseConcisePreprocessorDirectivesCheck.h
    
clang-tools-extra/docs/clang-tidy/checks/readability/use-concise-preprocessor-directives.rst
    
clang-tools-extra/test/clang-tidy/checkers/readability/use-concise-preprocessor-directives.cpp

Modified: 
    clang-tools-extra/clang-tidy/readability/CMakeLists.txt
    clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
    clang-tools-extra/docs/ReleaseNotes.rst
    clang-tools-extra/docs/clang-tidy/checks/list.rst

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt 
b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index 4be1a8f831339..4b4c49d3b17d1 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -58,6 +58,7 @@ add_clang_library(clangTidyReadabilityModule STATIC
   UniqueptrDeleteReleaseCheck.cpp
   UppercaseLiteralSuffixCheck.cpp
   UseAnyOfAllOfCheck.cpp
+  UseConcisePreprocessorDirectivesCheck.cpp
   UseStdMinMaxCheck.cpp
 
   LINK_LIBS

diff  --git 
a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp 
b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index d59b0312673b9..12f8cdb289dd2 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -61,6 +61,7 @@
 #include "UniqueptrDeleteReleaseCheck.h"
 #include "UppercaseLiteralSuffixCheck.h"
 #include "UseAnyOfAllOfCheck.h"
+#include "UseConcisePreprocessorDirectivesCheck.h"
 #include "UseStdMinMaxCheck.h"
 
 namespace clang::tidy {
@@ -173,6 +174,8 @@ class ReadabilityModule : public ClangTidyModule {
         "readability-uppercase-literal-suffix");
     CheckFactories.registerCheck<UseAnyOfAllOfCheck>(
         "readability-use-anyofallof");
+    CheckFactories.registerCheck<UseConcisePreprocessorDirectivesCheck>(
+        "readability-use-concise-preprocessor-directives");
     CheckFactories.registerCheck<UseStdMinMaxCheck>(
         "readability-use-std-min-max");
   }

diff  --git 
a/clang-tools-extra/clang-tidy/readability/UseConcisePreprocessorDirectivesCheck.cpp
 
b/clang-tools-extra/clang-tidy/readability/UseConcisePreprocessorDirectivesCheck.cpp
new file mode 100644
index 0000000000000..05c0088e6b41b
--- /dev/null
+++ 
b/clang-tools-extra/clang-tidy/readability/UseConcisePreprocessorDirectivesCheck.cpp
@@ -0,0 +1,110 @@
+//===--- UseConcisePreprocessorDirectivesCheck.cpp - clang-tidy 
-----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseConcisePreprocessorDirectivesCheck.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+
+#include <array>
+
+namespace clang::tidy::readability {
+
+namespace {
+
+class IfPreprocessorCallbacks final : public PPCallbacks {
+public:
+  IfPreprocessorCallbacks(ClangTidyCheck &Check, const Preprocessor &PP)
+      : Check(Check), PP(PP) {}
+
+  void If(SourceLocation Loc, SourceRange ConditionRange,
+          ConditionValueKind) override {
+    impl(Loc, ConditionRange, {"ifdef", "ifndef"});
+  }
+
+  void Elif(SourceLocation Loc, SourceRange ConditionRange, ConditionValueKind,
+            SourceLocation) override {
+    if (PP.getLangOpts().C23 || PP.getLangOpts().CPlusPlus23)
+      impl(Loc, ConditionRange, {"elifdef", "elifndef"});
+  }
+
+private:
+  void impl(SourceLocation DirectiveLoc, SourceRange ConditionRange,
+            const std::array<llvm::StringLiteral, 2> &Replacements) {
+    // Lexer requires its input range to be null-terminated.
+    SmallString<128> Condition =
+        Lexer::getSourceText(CharSourceRange::getTokenRange(ConditionRange),
+                             PP.getSourceManager(), PP.getLangOpts());
+    Condition.push_back('\0');
+    Lexer Lex(DirectiveLoc, PP.getLangOpts(), Condition.data(),
+              Condition.data(), Condition.data() + Condition.size() - 1);
+    Token Tok;
+    bool Inverted = false; // The inverted form of #*def is #*ndef.
+    std::size_t ParensNestingDepth = 0;
+    for (;;) {
+      if (Lex.LexFromRawLexer(Tok))
+        return;
+
+      if (Tok.is(tok::TokenKind::exclaim) ||
+          (PP.getLangOpts().CPlusPlus &&
+           Tok.is(tok::TokenKind::raw_identifier) &&
+           Tok.getRawIdentifier() == "not"))
+        Inverted = !Inverted;
+      else if (Tok.is(tok::TokenKind::l_paren))
+        ++ParensNestingDepth;
+      else
+        break;
+    }
+
+    if (Tok.isNot(tok::TokenKind::raw_identifier) ||
+        Tok.getRawIdentifier() != "defined")
+      return;
+
+    bool NoMoreTokens = Lex.LexFromRawLexer(Tok);
+    if (Tok.is(tok::TokenKind::l_paren)) {
+      if (NoMoreTokens)
+        return;
+      ++ParensNestingDepth;
+      NoMoreTokens = Lex.LexFromRawLexer(Tok);
+    }
+
+    if (Tok.isNot(tok::TokenKind::raw_identifier))
+      return;
+    const StringRef Macro = Tok.getRawIdentifier();
+
+    while (!NoMoreTokens) {
+      NoMoreTokens = Lex.LexFromRawLexer(Tok);
+      if (Tok.isNot(tok::TokenKind::r_paren))
+        return;
+      --ParensNestingDepth;
+    }
+
+    if (ParensNestingDepth != 0)
+      return;
+
+    Check.diag(
+        DirectiveLoc,
+        "preprocessor condition can be written more concisely using '#%0'")
+        << FixItHint::CreateReplacement(DirectiveLoc, Replacements[Inverted])
+        << FixItHint::CreateReplacement(ConditionRange, Macro)
+        << Replacements[Inverted];
+  }
+
+  ClangTidyCheck &Check;
+  const Preprocessor &PP;
+};
+
+} // namespace
+
+void UseConcisePreprocessorDirectivesCheck::registerPPCallbacks(
+    const SourceManager &, Preprocessor *PP, Preprocessor *) {
+  PP->addPPCallbacks(std::make_unique<IfPreprocessorCallbacks>(*this, *PP));
+}
+
+} // namespace clang::tidy::readability

diff  --git 
a/clang-tools-extra/clang-tidy/readability/UseConcisePreprocessorDirectivesCheck.h
 
b/clang-tools-extra/clang-tidy/readability/UseConcisePreprocessorDirectivesCheck.h
new file mode 100644
index 0000000000000..e65b16876a89a
--- /dev/null
+++ 
b/clang-tools-extra/clang-tidy/readability/UseConcisePreprocessorDirectivesCheck.h
@@ -0,0 +1,34 @@
+//===--- UseConcisePreprocessorDirectivesCheck.h - clang-tidy ---*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef 
LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USECONCISEPREPROCESSORDIRECTIVESCHECK_H
+#define 
LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USECONCISEPREPROCESSORDIRECTIVESCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Finds uses of ``#if`` that can be simplified to ``#ifdef`` or ``#ifndef``
+/// and, since C23 and C++23, uses of ``#elif`` that can be simplified to
+/// ``#elifdef`` or ``#elifndef``.
+///
+/// User-facing documentation:
+/// 
https://clang.llvm.org/extra/clang-tidy/checks/readability/use-concise-preprocessor-directives.html
+class UseConcisePreprocessorDirectivesCheck : public ClangTidyCheck {
+public:
+  using ClangTidyCheck::ClangTidyCheck;
+  void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+                           Preprocessor *ModuleExpanderPP) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return true;
+  }
+};
+
+} // namespace clang::tidy::readability
+
+#endif // 
LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USECONCISEPREPROCESSORDIRECTIVESCHECK_H

diff  --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index d76cd3a7c8728..ee7329fa8610b 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -166,6 +166,13 @@ New checks
   Finds potentially erroneous calls to ``reset`` method on smart pointers when
   the pointee type also has a ``reset`` method.
 
+- New :doc:`readability-use-concise-preprocessor-directives
+  <clang-tidy/checks/readability/use-concise-preprocessor-directives>` check.
+
+  Finds uses of ``#if`` that can be simplified to ``#ifdef`` or ``#ifndef`` 
and,
+  since C23 and C++23, uses of ``#elif`` that can be simplified to ``#elifdef``
+  or ``#elifndef``.
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst 
b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 9b62c87664693..0cffbd323caa2 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -411,6 +411,7 @@ Clang-Tidy Checks
    :doc:`readability-uniqueptr-delete-release 
<readability/uniqueptr-delete-release>`, "Yes"
    :doc:`readability-uppercase-literal-suffix 
<readability/uppercase-literal-suffix>`, "Yes"
    :doc:`readability-use-anyofallof <readability/use-anyofallof>`,
+   :doc:`readability-use-concise-preprocessor-directives 
<readability/use-concise-preprocessor-directives>`, "Yes"
    :doc:`readability-use-std-min-max <readability/use-std-min-max>`, "Yes"
    :doc:`zircon-temporary-objects <zircon/temporary-objects>`,
 

diff  --git 
a/clang-tools-extra/docs/clang-tidy/checks/readability/use-concise-preprocessor-directives.rst
 
b/clang-tools-extra/docs/clang-tidy/checks/readability/use-concise-preprocessor-directives.rst
new file mode 100644
index 0000000000000..30ec7e6b89936
--- /dev/null
+++ 
b/clang-tools-extra/docs/clang-tidy/checks/readability/use-concise-preprocessor-directives.rst
@@ -0,0 +1,30 @@
+.. title:: clang-tidy - readability-use-concise-preprocessor-directives
+
+readability-use-concise-preprocessor-directives
+===============================================
+
+Finds uses of ``#if`` that can be simplified to ``#ifdef`` or ``#ifndef`` and,
+since C23 and C++23, uses of ``#elif`` that can be simplified to ``#elifdef``
+or ``#elifndef``:
+
+.. code-block:: c++
+
+  #if defined(MEOW)
+  #if !defined(MEOW)
+
+  // becomes
+
+  #ifdef MEOW
+  #ifndef MEOW
+
+Since C23 and C++23:
+
+.. code-block:: c++
+
+  #elif defined(MEOW)
+  #elif !defined(MEOW)
+
+  // becomes
+
+  #elifdef MEOW
+  #elifndef MEOW

diff  --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/use-concise-preprocessor-directives.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/use-concise-preprocessor-directives.cpp
new file mode 100644
index 0000000000000..53e079bcca40f
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/use-concise-preprocessor-directives.cpp
@@ -0,0 +1,144 @@
+// RUN: %check_clang_tidy -std=c++98,c++11,c++14,c++17,c++20 
-check-suffixes=,CXX %s readability-use-concise-preprocessor-directives %t
+// RUN: %check_clang_tidy -std=c++23-or-later -check-suffixes=,23,CXX,CXX23 %s 
readability-use-concise-preprocessor-directives %t
+
+// RUN: %check_clang_tidy -std=c99,c11,c17 %s 
readability-use-concise-preprocessor-directives %t -- -- -x c
+// RUN: %check_clang_tidy -std=c23-or-later -check-suffixes=,23 %s 
readability-use-concise-preprocessor-directives %t -- -- -x c
+
+// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#ifdef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES: #ifdef FOO
+#if defined(FOO)
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#elifdef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifdef BAR
+#elif defined(BAR)
+#endif
+
+// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#ifdef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES: #ifdef FOO
+#if defined FOO
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#elifdef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifdef BAR
+#elif defined BAR
+#endif
+
+// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#ifdef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES: #ifdef FOO
+#if (defined(FOO))
+// CHECK-MESSAGES-23: :[[@LINE+2]]:4: warning: preprocessor condition can be 
written more concisely using '#elifdef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #  elifdef BAR
+#  elif (defined(BAR))
+#endif
+
+// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#ifdef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES: #ifdef FOO
+#if (defined FOO)
+// CHECK-MESSAGES-23: :[[@LINE+2]]:4: warning: preprocessor condition can be 
written more concisely using '#elifdef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #  elifdef BAR
+#  elif (defined BAR)
+#endif
+
+// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#ifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES: #ifndef FOO
+#if !defined(FOO)
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#elifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifndef BAR
+#elif !defined(BAR)
+#endif
+
+#ifdef __cplusplus
+// CHECK-MESSAGES-CXX: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#ifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-CXX: #ifndef FOO
+#if not defined(FOO)
+// CHECK-MESSAGES-CXX23: :[[@LINE+2]]:2: warning: preprocessor condition can 
be written more concisely using '#elifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-CXX23: #elifndef BAR
+#elif not defined(BAR)
+#endif
+#endif // __cplusplus
+
+// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#ifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES: #ifndef FOO
+#if !defined FOO
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#elifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifndef BAR
+#elif !defined BAR
+#endif
+
+#ifdef __cplusplus
+// CHECK-MESSAGES-CXX: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#ifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-CXX: #ifndef FOO
+#if not defined FOO
+// CHECK-MESSAGES-CXX23: :[[@LINE+2]]:2: warning: preprocessor condition can 
be written more concisely using '#elifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-CXX23: #elifndef BAR
+#elif not defined BAR
+#endif
+#endif // __cplusplus
+
+// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#ifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES: #ifndef FOO
+#if (!defined(FOO))
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#elifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifndef BAR
+#elif (!defined(BAR))
+#endif
+
+// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#ifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES: #ifndef FOO
+#if (!defined FOO)
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#elifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifndef BAR
+#elif (!defined BAR)
+#endif
+
+// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#ifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES: #ifndef FOO
+#if !(defined(FOO))
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#elifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifndef BAR
+#elif !(defined(BAR))
+#endif
+
+// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#ifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES: #ifndef FOO
+#if !(defined FOO)
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#elifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifndef BAR
+#elif !(defined BAR)
+#endif
+
+// These cases with many parentheses and negations are unrealistic, but
+// handling them doesn't really add any complexity to the implementation.
+// Test them for good measure.
+
+// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#ifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES: #ifndef FOO
+#if !((!!(defined(FOO))))
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#elifdef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifdef BAR
+#elif ((!(!(defined(BAR)))))
+#endif
+
+// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#ifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES: #ifndef FOO
+#if !((!!(defined FOO)))
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#elifdef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifdef BAR
+#elif ((!(!(defined BAR))))
+#endif
+
+// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#ifndef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES: #ifndef FOO
+#if !(  (!! ( defined FOO  )) )
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be 
written more concisely using '#elifdef' 
[readability-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifdef BAR
+#elif   ( (  !(!( defined   BAR) )  ))
+#endif
+
+#if FOO
+#elif BAR
+#endif
+
+#if defined(FOO) && defined(BAR)
+#elif defined(FOO) && defined(BAR)
+#endif
+
+#if defined FOO && BAR
+#endif


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to