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