aboutsummaryrefslogtreecommitdiff
path: root/clang-tools-extra/clang-tidy/readability
diff options
context:
space:
mode:
authorVictor Chernyakin <chernyakin.victor.j@outlook.com>2025-07-13 09:23:27 -0700
committerGitHub <noreply@github.com>2025-07-13 19:23:27 +0300
commit59b39c0031eded7c46e554b161382187cb2d0ca5 (patch)
treeb240dd3f6b36b81cd981a9266ba1f23c958daf4f /clang-tools-extra/clang-tidy/readability
parent1db33f1034aa758ff7a04d09641232d2ce1a2a2b (diff)
downloadllvm-59b39c0031eded7c46e554b161382187cb2d0ca5.zip
llvm-59b39c0031eded7c46e554b161382187cb2d0ca5.tar.gz
llvm-59b39c0031eded7c46e554b161382187cb2d0ca5.tar.bz2
[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 ```
Diffstat (limited to 'clang-tools-extra/clang-tidy/readability')
-rw-r--r--clang-tools-extra/clang-tidy/readability/CMakeLists.txt1
-rw-r--r--clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp3
-rw-r--r--clang-tools-extra/clang-tidy/readability/UseConcisePreprocessorDirectivesCheck.cpp110
-rw-r--r--clang-tools-extra/clang-tidy/readability/UseConcisePreprocessorDirectivesCheck.h34
4 files changed, 148 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index 4be1a8f..4b4c49d 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 d59b031..12f8cdb 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 @@ public:
"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 0000000..05c0088e
--- /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 0000000..e65b168
--- /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