Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

localspook
Copy link
Contributor

@localspook localspook commented Jul 3, 2025

Closes #132561.

This is a check that rewrites #ifs and #elifs like so:

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

And, since C23 and C++23:

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

We can bikeshed the name and category. For example, modernize isn't ideal; rewriting #if defined(A) as #ifdef A isn't
really "modernizing", because #ifdef has always existed, but rewriting #elif defined(A) as #elifdef A
certainly is.
Moved into readability category.

…tives`

Rewrites preprocessor conditions like
`#if defined(MEOW)` as `#ifdef MEOW` and
`#elif !defined(MEOW)` as `#elifndef MEOW`.

Closes llvm#132561.
Copy link

github-actions bot commented Jul 3, 2025

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot
Copy link
Member

llvmbot commented Jul 3, 2025

@llvm/pr-subscribers-clang-tools-extra

@llvm/pr-subscribers-clang-tidy

Author: Victor Chernyakin (localspook)

Changes

Closes #132561.

This is a check that rewrites #ifs and #elifs like so:

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

And, since C23 and C++23:

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

We can bikeshed the name and category. For example, modernize isn't ideal; rewriting #if defined(A) as #ifdef A isn't
really "modernizing", because #ifdef has always existed, but rewriting #elif defined(A) as #elifdef A
certainly is.


Full diff: https://github.com/llvm/llvm-project/pull/146830.diff

8 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/modernize/CMakeLists.txt (+1)
  • (modified) clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp (+3)
  • (added) clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.cpp (+105)
  • (added) clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.h (+37)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+6)
  • (modified) clang-tools-extra/docs/clang-tidy/checks/list.rst (+1)
  • (added) clang-tools-extra/docs/clang-tidy/checks/modernize/use-concise-preprocessor-directives.rst (+28)
  • (added) clang-tools-extra/test/clang-tidy/checkers/modernize/use-concise-preprocessor-directives.cpp (+134)
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index 619a27b2f9bb6..22d5214b61441 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -30,6 +30,7 @@ add_clang_library(clangTidyModernizeModule STATIC
   UnaryStaticAssertCheck.cpp
   UseAutoCheck.cpp
   UseBoolLiteralsCheck.cpp
+  UseConcisePreprocessorDirectivesCheck.cpp
   UseConstraintsCheck.cpp
   UseDefaultMemberInitCheck.cpp
   UseDesignatedInitializersCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index fdf38bc4b6308..28c5467f7b3e0 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -31,6 +31,7 @@
 #include "UnaryStaticAssertCheck.h"
 #include "UseAutoCheck.h"
 #include "UseBoolLiteralsCheck.h"
+#include "UseConcisePreprocessorDirectivesCheck.h"
 #include "UseConstraintsCheck.h"
 #include "UseDefaultMemberInitCheck.h"
 #include "UseDesignatedInitializersCheck.h"
@@ -76,6 +77,8 @@ class ModernizeModule : public ClangTidyModule {
     CheckFactories.registerCheck<MinMaxUseInitializerListCheck>(
         "modernize-min-max-use-initializer-list");
     CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value");
+    CheckFactories.registerCheck<UseConcisePreprocessorDirectivesCheck>(
+        "modernize-use-concise-preprocessor-directives");
     CheckFactories.registerCheck<UseDesignatedInitializersCheck>(
         "modernize-use-designated-initializers");
     CheckFactories.registerCheck<UseIntegerSignComparisonCheck>(
diff --git a/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.cpp
new file mode 100644
index 0000000000000..56ed1b3cc879d
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.cpp
@@ -0,0 +1,105 @@
+//===--- 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"
+
+namespace clang::tidy::modernize {
+
+namespace {
+
+class IfPreprocessorCallbacks final : public PPCallbacks {
+public:
+  IfPreprocessorCallbacks(ClangTidyCheck &Check, 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 llvm::StringLiteral (&Replacements)[2]) {
+    StringRef Condition =
+        Lexer::getSourceText(CharSourceRange::getTokenRange(ConditionRange),
+                             PP.getSourceManager(), PP.getLangOpts());
+    Lexer Lex(DirectiveLoc, PP.getLangOpts(), Condition.data(),
+              Condition.data(), Condition.data() + Condition.size());
+    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;
+    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")
+        << FixItHint::CreateReplacement(DirectiveLoc, Replacements[Inverted])
+        << FixItHint::CreateReplacement(ConditionRange, Macro);
+  }
+
+  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::modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.h b/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.h
new file mode 100644
index 0000000000000..cbc96dc930f82
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.h
@@ -0,0 +1,37 @@
+//===--- 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_MODERNIZE_USECONCISEPREPROCESSORDIRECTIVESCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USECONCISEPREPROCESSORDIRECTIVESCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::modernize {
+
+/// Shortens `#if` preprocessor conditions:
+///
+/// #if  defined(MEOW) -> #ifdef  MEOW
+/// #if !defined(MEOW) -> #ifndef MEOW
+///
+/// And, since C23 and C++23, shortens `#elif` conditions too:
+///
+/// #elif  defined(MEOW) -> #elifdef  MEOW
+/// #elif !defined(MEOW) -> #elifndef MEOW
+///
+/// User-facing documentation:
+/// https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-concise-preprocessor-directives.html
+class UseConcisePreprocessorDirectivesCheck : public ClangTidyCheck {
+public:
+  using ClangTidyCheck::ClangTidyCheck;
+  void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+                           Preprocessor *ModuleExpanderPP) override;
+};
+
+} // namespace clang::tidy::modernize
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USECONCISEPREPROCESSORDIRECTIVESCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 198efee7754de..331ef618a443b 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -142,6 +142,12 @@ New checks
   Finds unscoped (non-class) ``enum`` declarations and suggests using
   ``enum class`` instead.
 
+- New :doc:`modernize-use-concise-preprocessor-directives
+  <clang-tidy/checks/modernize/use-concise-preprocessor-directives>` check.
+
+  Rewrites preprocessor conditions like ``#if defined(MEOW)`` as ``#ifdef MEOW``
+  and ``#elif !defined(MEOW)`` as ``#elifndef MEOW``.
+
 - New :doc:`modernize-use-scoped-lock
   <clang-tidy/checks/modernize/use-scoped-lock>` check.
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 5098582d0c42b..e0227b0478d99 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -300,6 +300,7 @@ Clang-Tidy Checks
    :doc:`modernize-unary-static-assert <modernize/unary-static-assert>`, "Yes"
    :doc:`modernize-use-auto <modernize/use-auto>`, "Yes"
    :doc:`modernize-use-bool-literals <modernize/use-bool-literals>`, "Yes"
+   :doc:`modernize-use-concise-preprocessor-directives <modernize/use-concise-preprocessor-directives>`, "Yes"
    :doc:`modernize-use-constraints <modernize/use-constraints>`, "Yes"
    :doc:`modernize-use-default-member-init <modernize/use-default-member-init>`, "Yes"
    :doc:`modernize-use-designated-initializers <modernize/use-designated-initializers>`, "Yes"
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-concise-preprocessor-directives.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-concise-preprocessor-directives.rst
new file mode 100644
index 0000000000000..04c8e8af38153
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-concise-preprocessor-directives.rst
@@ -0,0 +1,28 @@
+.. title:: clang-tidy - modernize-use-concise-preprocessor-directives
+
+modernize-use-concise-preprocessor-directives
+=============================================
+
+Shortens `#if` preprocessor conditions:
+
+.. code-block:: c++
+
+  #if defined(MEOW)
+  #if !defined(MEOW)
+
+  // becomes
+
+  #ifdef MEOW
+  #ifndef MEOW
+
+And, since C23 and C++23, shortens `#elif` conditions too:
+
+.. code-block:: c++
+
+  #elif defined(MEOW)
+  #elif !defined(MEOW)
+
+  // becomes
+
+  #elifdef MEOW
+  #elifndef MEOW
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-concise-preprocessor-directives.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-concise-preprocessor-directives.cpp
new file mode 100644
index 0000000000000..3262bd0388680
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-concise-preprocessor-directives.cpp
@@ -0,0 +1,134 @@
+// RUN: %check_clang_tidy -std=c++98 -check-suffixes=ALL,CXX %s modernize-use-concise-preprocessor-directives %t
+// RUN: %check_clang_tidy -std=c++11 -check-suffixes=ALL,CXX %s modernize-use-concise-preprocessor-directives %t
+// RUN: %check_clang_tidy -std=c++14 -check-suffixes=ALL,CXX %s modernize-use-concise-preprocessor-directives %t
+// RUN: %check_clang_tidy -std=c++17 -check-suffixes=ALL,CXX %s modernize-use-concise-preprocessor-directives %t
+// RUN: %check_clang_tidy -std=c++20 -check-suffixes=ALL,CXX %s modernize-use-concise-preprocessor-directives %t
+// RUN: %check_clang_tidy -std=c++23-or-later -check-suffixes=ALL,23,CXX,CXX23 %s modernize-use-concise-preprocessor-directives %t
+
+// RUN: %check_clang_tidy -std=c99 -check-suffix=ALL %s modernize-use-concise-preprocessor-directives %t -- -- -x c
+// RUN: %check_clang_tidy -std=c11 -check-suffix=ALL %s modernize-use-concise-preprocessor-directives %t -- -- -x c
+// RUN: %check_clang_tidy -std=c23-or-later -check-suffix=ALL,23 %s modernize-use-concise-preprocessor-directives %t -- -- -x c
+
+// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-ALL: #ifdef FOO
+#if defined(FOO)
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifdef BAR
+#elif defined(BAR)
+#endif
+
+// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-ALL: #ifdef FOO
+#if defined FOO
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifdef BAR
+#elif defined BAR
+#endif
+
+// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-ALL: #ifdef FOO
+#if (defined(FOO))
+// CHECK-MESSAGES-23: :[[@LINE+2]]:4: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #  elifdef BAR
+#  elif (defined(BAR))
+#endif
+
+// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-ALL: #ifdef FOO
+#if (defined FOO)
+// CHECK-MESSAGES-23: :[[@LINE+2]]:4: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #  elifdef BAR
+#  elif (defined BAR)
+#endif
+
+// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-ALL: #ifndef FOO
+#if !defined(FOO)
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-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 [modernize-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 [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-CXX23: #elifndef BAR
+#elif not defined(BAR)
+#endif
+#endif // __cplusplus
+
+// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-ALL: #ifndef FOO
+#if !defined FOO
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-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 [modernize-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 [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-CXX23: #elifndef BAR
+#elif not defined BAR
+#endif
+#endif // __cplusplus
+
+// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-ALL: #ifndef FOO
+#if (!defined(FOO))
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifndef BAR
+#elif (!defined(BAR))
+#endif
+
+// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-ALL: #ifndef FOO
+#if (!defined FOO)
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifndef BAR
+#elif (!defined BAR)
+#endif
+
+// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-ALL: #ifndef FOO
+#if !(defined(FOO))
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifndef BAR
+#elif !(defined(BAR))
+#endif
+
+// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-ALL: #ifndef FOO
+#if !(defined FOO)
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-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-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-ALL: #ifndef FOO
+#if !((!!(defined(FOO))))
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifdef BAR
+#elif ((!(!(defined(BAR)))))
+#endif
+
+// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-ALL: #ifndef FOO
+#if !((!!(defined FOO)))
+// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
+// CHECK-FIXES-23: #elifdef BAR
+#elif ((!(!(defined BAR))))
+#endif
+
+#if FOO
+#elif BAR
+#endif

@localspook localspook marked this pull request as draft July 3, 2025 08:24
@vbvictor
Copy link
Contributor

vbvictor commented Jul 3, 2025

If you are finished with the work on the check, please convert PR to "ready for review".

I feel that this check should be readability section.

@localspook localspook marked this pull request as ready for review July 3, 2025 22:27
@carlosgalvezp
Copy link
Contributor

+1 to moving to readability.

What's the behavior when you have:

#if defined(foo) && defined(bar)

? I don't believe I saw a test for this use case.

As a user I would probably prefer to keep it as is instead of having one ifdef and one if defined. Perhaps this can be configured as it's a highly debatable/subjective topic.

@localspook
Copy link
Contributor Author

Maybe I'm misunderstanding, but are you concerned about

#if defined(foo) && defined(bar)

being rewritten to this?

#ifdef foo && if defined(bar)

It won't do that, that's not valid syntax (there's not really any way to rewrite it). I've added a test to ensure the check doesn't touch it.

@localspook localspook changed the title [clang-tidy] Add new check: modernize-use-concise-preprocessor-directives [clang-tidy] Add new check: readability-use-concise-preprocessor-directives Jul 5, 2025
Copy link
Contributor

@5chmidti 5chmidti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM minus the open conversation

Copy link
Contributor

@vbvictor vbvictor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@localspook
Copy link
Contributor Author

When we're ready to merge, someone please do it for me, I don't have write permissions.

Copy link
Contributor

@carlosgalvezp carlosgalvezp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[clang-tidy] Check request: modernize-use-elifdef-and-elifndef
6 participants