[clang-tools-extra] [clang-tidy] Add new check: `modernize-use-concise-preprocessor-directives` (PR #146830)
Victor Chernyakin via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 3 15:18:45 PDT 2025
https://github.com/localspook updated https://github.com/llvm/llvm-project/pull/146830
>From 9eb58438d2b4061ad7a6bdbd1db82a5fd4043948 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Thu, 3 Jul 2025 00:19:52 -0700
Subject: [PATCH 1/5] [clang-tidy] Add new check:
`modernize-use-concise-preprocessor-directives`
Rewrites preprocessor conditions like
`#if defined(MEOW)` as `#ifdef MEOW` and
`#elif !defined(MEOW)` as `#elifndef MEOW`.
Closes #132561.
---
.../clang-tidy/modernize/CMakeLists.txt | 1 +
.../modernize/ModernizeTidyModule.cpp | 3 +
.../UseConcisePreprocessorDirectivesCheck.cpp | 105 ++++++++++++++
.../UseConcisePreprocessorDirectivesCheck.h | 37 +++++
clang-tools-extra/docs/ReleaseNotes.rst | 6 +
.../docs/clang-tidy/checks/list.rst | 1 +
.../use-concise-preprocessor-directives.rst | 28 ++++
.../use-concise-preprocessor-directives.cpp | 134 ++++++++++++++++++
8 files changed, 315 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-concise-preprocessor-directives.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-concise-preprocessor-directives.cpp
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
>From 8e1c675c1655236e2aba61b1f4e7ff37226af33b Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Thu, 3 Jul 2025 13:17:24 -0700
Subject: [PATCH 2/5] Specify `isLanguageVersionSupported`
---
.../modernize/UseConcisePreprocessorDirectivesCheck.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.h b/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.h
index cbc96dc930f82..9550238266b9d 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.h
@@ -30,6 +30,9 @@ class UseConcisePreprocessorDirectivesCheck : public ClangTidyCheck {
using ClangTidyCheck::ClangTidyCheck;
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) override;
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus || LangOpts.C99;
+ }
};
} // namespace clang::tidy::modernize
>From 7d42a2fb5f179667c6c84c2102a62bc82663efcd Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Thu, 3 Jul 2025 13:20:55 -0700
Subject: [PATCH 3/5] Const correctness
---
.../modernize/UseConcisePreprocessorDirectivesCheck.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.cpp
index 56ed1b3cc879d..c9d043f672e2c 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.cpp
@@ -18,7 +18,7 @@ namespace {
class IfPreprocessorCallbacks final : public PPCallbacks {
public:
- IfPreprocessorCallbacks(ClangTidyCheck &Check, Preprocessor &PP)
+ IfPreprocessorCallbacks(ClangTidyCheck &Check, const Preprocessor &PP)
: Check(Check), PP(PP) {}
void If(SourceLocation Loc, SourceRange ConditionRange,
@@ -36,7 +36,7 @@ class IfPreprocessorCallbacks final : public PPCallbacks {
private:
void impl(SourceLocation DirectiveLoc, SourceRange ConditionRange,
const llvm::StringLiteral (&Replacements)[2]) {
- StringRef Condition =
+ const StringRef Condition =
Lexer::getSourceText(CharSourceRange::getTokenRange(ConditionRange),
PP.getSourceManager(), PP.getLangOpts());
Lexer Lex(DirectiveLoc, PP.getLangOpts(), Condition.data(),
@@ -73,7 +73,7 @@ class IfPreprocessorCallbacks final : public PPCallbacks {
if (Tok.isNot(tok::TokenKind::raw_identifier))
return;
- StringRef Macro = Tok.getRawIdentifier();
+ const StringRef Macro = Tok.getRawIdentifier();
while (!NoMoreTokens) {
NoMoreTokens = Lex.LexFromRawLexer(Tok);
>From 43de4574d2a638dd8b086b9183663bb02f85e28f Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Thu, 3 Jul 2025 13:42:03 -0700
Subject: [PATCH 4/5] Align release notes and docs
---
clang-tools-extra/docs/ReleaseNotes.rst | 5 +++--
.../modernize/use-concise-preprocessor-directives.rst | 6 ++++--
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 331ef618a443b..c3967e86e2557 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -145,8 +145,9 @@ New checks
- 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``.
+ Finds uses of ``#if`` that be simplified to ``#ifdef`` or ``#ifndef`` and,
+ since C23 and C++23, uses of ``#elif`` that can be simplified to ``#elifdef``
+ or ``#elifndef``.
- New :doc:`modernize-use-scoped-lock
<clang-tidy/checks/modernize/use-scoped-lock>` check.
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
index 04c8e8af38153..a486fce3b0f66 100644
--- 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
@@ -3,7 +3,9 @@
modernize-use-concise-preprocessor-directives
=============================================
-Shortens `#if` preprocessor conditions:
+Finds uses of ``#if`` that 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++
@@ -15,7 +17,7 @@ Shortens `#if` preprocessor conditions:
#ifdef MEOW
#ifndef MEOW
-And, since C23 and C++23, shortens `#elif` conditions too:
+Since C23 and C++23:
.. code-block:: c++
>From 3ef8a8dd8e0bf5304ca3313a34d37cf5e4b82656 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Thu, 3 Jul 2025 15:13:17 -0700
Subject: [PATCH 5/5] Fix assert failure
---
.../modernize/UseConcisePreprocessorDirectivesCheck.cpp | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.cpp
index c9d043f672e2c..94686772d7931 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseConcisePreprocessorDirectivesCheck.cpp
@@ -36,11 +36,13 @@ class IfPreprocessorCallbacks final : public PPCallbacks {
private:
void impl(SourceLocation DirectiveLoc, SourceRange ConditionRange,
const llvm::StringLiteral (&Replacements)[2]) {
- const StringRef Condition =
+ // 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());
+ Condition.data(), Condition.data() + Condition.size() - 1);
Token Tok;
bool Inverted = false; // The inverted form of #*def is #*ndef.
std::size_t ParensNestingDepth = 0;
More information about the cfe-commits
mailing list