llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-tools-extra @llvm/pr-subscribers-clang-tidy Author: Victor Chernyakin (localspook) <details> <summary>Changes</summary> 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 ``` 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 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 `````````` </details> https://github.com/llvm/llvm-project/pull/146830 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits