https://github.com/unterumarmung updated https://github.com/llvm/llvm-project/pull/189743
>From fedc71274dfefc12d845e88ac279ad6fb275b1fc Mon Sep 17 00:00:00 2001 From: Daniil Dudkin <[email protected]> Date: Tue, 31 Mar 2026 22:56:57 +0300 Subject: [PATCH 1/2] [clang-tidy] Add modernize-use-if-consteval check --- .../clang-tidy/modernize/CMakeLists.txt | 1 + .../modernize/ModernizeTidyModule.cpp | 3 + .../modernize/UseIfConstevalCheck.cpp | 136 +++++++++ .../modernize/UseIfConstevalCheck.h | 36 +++ clang-tools-extra/docs/ReleaseNotes.rst | 5 + .../docs/clang-tidy/checks/list.rst | 1 + .../checks/modernize/use-if-consteval.rst | 47 ++++ .../checkers/modernize/use-if-consteval.cpp | 263 ++++++++++++++++++ 8 files changed, 492 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/modernize/UseIfConstevalCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/modernize/UseIfConstevalCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-if-consteval.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-if-consteval.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index 2c5c44db587fe..9e15c9ae13123 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -39,6 +39,7 @@ add_clang_library(clangTidyModernizeModule STATIC UseEmplaceCheck.cpp UseEqualsDefaultCheck.cpp UseEqualsDeleteCheck.cpp + UseIfConstevalCheck.cpp UseIntegerSignComparisonCheck.cpp UseNodiscardCheck.cpp UseNoexceptCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index cc13da7535bcb..6c786c9b9f596 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -39,6 +39,7 @@ #include "UseEmplaceCheck.h" #include "UseEqualsDefaultCheck.h" #include "UseEqualsDeleteCheck.h" +#include "UseIfConstevalCheck.h" #include "UseIntegerSignComparisonCheck.h" #include "UseNodiscardCheck.h" #include "UseNoexceptCheck.h" @@ -90,6 +91,8 @@ class ModernizeModule : public ClangTidyModule { CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value"); CheckFactories.registerCheck<UseDesignatedInitializersCheck>( "modernize-use-designated-initializers"); + CheckFactories.registerCheck<UseIfConstevalCheck>( + "modernize-use-if-consteval"); CheckFactories.registerCheck<UseIntegerSignComparisonCheck>( "modernize-use-integer-sign-comparison"); CheckFactories.registerCheck<UseRangesCheck>("modernize-use-ranges"); diff --git a/clang-tools-extra/clang-tidy/modernize/UseIfConstevalCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseIfConstevalCheck.cpp new file mode 100644 index 0000000000000..b31a4b97a01ff --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseIfConstevalCheck.cpp @@ -0,0 +1,136 @@ +//===----------------------------------------------------------------------===// +// +// 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 "UseIfConstevalCheck.h" + +#include "../utils/BracesAroundStatement.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::modernize { + +namespace { + +struct BraceFix { + bool NeedsBraces = false; + utils::BraceInsertionHints Hints; +}; + +} // namespace + +static const Stmt *ignoreAttributedStmt(const Stmt *S) { + while (const auto *AS = dyn_cast_or_null<AttributedStmt>(S)) + S = AS->getSubStmt(); + return S; +} + +static std::optional<CharSourceRange> +getHeaderRange(const IfStmt *If, const SourceManager &SM, + const LangOptions &LangOpts) { + if (If->getIfLoc().isMacroID() || If->getRParenLoc().isMacroID()) + return std::nullopt; + + const CharSourceRange HeaderRange = Lexer::makeFileCharRange( + CharSourceRange::getTokenRange(If->getIfLoc(), If->getRParenLoc()), SM, + LangOpts); + if (HeaderRange.isInvalid()) + return std::nullopt; + return HeaderRange; +} + +static std::optional<BraceFix> +getBraceFix(const Stmt *S, const LangOptions &LangOpts, const SourceManager &SM, + SourceLocation StartLoc, + SourceLocation EndLocHint = SourceLocation()) { + S = ignoreAttributedStmt(S); + if (!S || isa<CompoundStmt>(S)) + return BraceFix(); + + const auto Hints = + utils::getBraceInsertionsHints(S, LangOpts, SM, StartLoc, EndLocHint); + if (!Hints || !Hints.offersFixIts()) + return std::nullopt; + + return BraceFix{true, Hints}; +} +void UseIfConstevalCheck::registerMatchers(MatchFinder *Finder) { + const auto IsConstantEvaluatedCall = + callExpr(callee(functionDecl(hasName("is_constant_evaluated"), + isInStdNamespace()))) + .bind("call"); + const auto IsNegatedConstantEvaluatedExpr = + unaryOperator(hasOperatorName("!"), hasUnaryOperand(ignoringParenImpCasts( + IsConstantEvaluatedCall))) + .bind("negation"); + const auto IsConstantEvaluatedExpr = + expr(anyOf(ignoringParenImpCasts(IsConstantEvaluatedCall), + ignoringParenImpCasts(IsNegatedConstantEvaluatedExpr))); + + Finder->addMatcher( + ifStmt(unless(anyOf(isConstexpr(), isConsteval())), + anyOf(hasCondition(IsConstantEvaluatedExpr), + hasConditionVariableStatement(declStmt(hasSingleDecl( + varDecl(hasInitializer(IsConstantEvaluatedExpr))))))) + .bind("if"), + this); +} + +void UseIfConstevalCheck::check(const MatchFinder::MatchResult &Result) { + const auto *If = Result.Nodes.getNodeAs<IfStmt>("if"); + const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call"); + assert(If && Call && "expected to match an if statement and its call"); + + const bool IsNegated = Result.Nodes.getNodeAs<UnaryOperator>("negation"); + const char *Replacement = IsNegated ? "if !consteval" : "if consteval"; + const SourceLocation DiagLoc = + Result.SourceManager->getExpansionLoc(Call->getExprLoc()); + + auto Diag = + diag( + DiagLoc, + "use '%0' instead of checking 'std::is_constant_evaluated()' in this " + "'if' statement") + << Replacement; + + if (If->hasInitStorage() || If->hasVarStorage()) + return; + + std::optional<CharSourceRange> HeaderRange = + getHeaderRange(If, *Result.SourceManager, getLangOpts()); + if (!HeaderRange) + return; + + std::optional<BraceFix> ThenBraceFix = + getBraceFix(If->getThen(), getLangOpts(), *Result.SourceManager, + If->getRParenLoc(), If->getElseLoc()); + if (!ThenBraceFix) + return; + + std::optional<BraceFix> ElseBraceFix = BraceFix(); + const Stmt *Else = ignoreAttributedStmt(If->getElse()); + if (Else && !isa<IfStmt>(Else)) { + ElseBraceFix = getBraceFix(If->getElse(), getLangOpts(), + *Result.SourceManager, If->getElseLoc()); + } + + std::string HeaderReplacement(Replacement); + if (ThenBraceFix->NeedsBraces) + HeaderReplacement += " {"; + Diag << FixItHint::CreateReplacement(*HeaderRange, HeaderReplacement); + + if (ThenBraceFix->NeedsBraces) + Diag << ThenBraceFix->Hints.closingBraceFixIt(); + + if (ElseBraceFix && ElseBraceFix->NeedsBraces) + Diag << ElseBraceFix->Hints.openingBraceFixIt() + << ElseBraceFix->Hints.closingBraceFixIt(); +} + +} // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/UseIfConstevalCheck.h b/clang-tools-extra/clang-tidy/modernize/UseIfConstevalCheck.h new file mode 100644 index 0000000000000..a87c9e6c5c8df --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseIfConstevalCheck.h @@ -0,0 +1,36 @@ +//===----------------------------------------------------------------------===// +// +// 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_USEIFCONSTEVALCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEIFCONSTEVALCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::modernize { + +/// Use if consteval instead of std::is_constant_evaluated in if statements. +/// +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-if-consteval.html +class UseIfConstevalCheck : public ClangTidyCheck { +public: + UseIfConstevalCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus23; + } + std::optional<TraversalKind> getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } +}; + +} // namespace clang::tidy::modernize + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEIFCONSTEVALCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 69dc5b9633398..64a3cf077b8bd 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -133,6 +133,11 @@ New checks ``llvm::to_vector(llvm::make_filter_range(...))`` that can be replaced with ``llvm::map_to_vector`` and ``llvm::filter_to_vector``. +- New :doc:`modernize-use-if-consteval + <clang-tidy/checks/modernize/use-if-consteval>` check. + + Use ``if consteval`` instead of ``std::is_constant_evaluated`` in if statements. + - New :doc:`modernize-use-std-bit <clang-tidy/checks/modernize/use-std-bit>` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 2b5be931271ec..8536ee08746ea 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -318,6 +318,7 @@ Clang-Tidy Checks :doc:`modernize-use-emplace <modernize/use-emplace>`, "Yes" :doc:`modernize-use-equals-default <modernize/use-equals-default>`, "Yes" :doc:`modernize-use-equals-delete <modernize/use-equals-delete>`, "Yes" + :doc:`modernize-use-if-consteval <modernize/use-if-consteval>`, "Yes" :doc:`modernize-use-integer-sign-comparison <modernize/use-integer-sign-comparison>`, "Yes" :doc:`modernize-use-nodiscard <modernize/use-nodiscard>`, "Yes" :doc:`modernize-use-noexcept <modernize/use-noexcept>`, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-if-consteval.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-if-consteval.rst new file mode 100644 index 0000000000000..6875b54025bc7 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-if-consteval.rst @@ -0,0 +1,47 @@ +.. title:: clang-tidy - modernize-use-if-consteval + +modernize-use-if-consteval +========================== + +Replaces direct ``std::is_constant_evaluated()`` checks in ``if`` statements +with C++23's ``if consteval`` syntax. + +In C++20, code often spells constant-evaluation branches as: + +.. code-block:: c++ + + if (std::is_constant_evaluated()) { + return slow_but_constexpr_path(); + } else { + return fast_runtime_path(); + } + +In C++23, the language has a dedicated spelling for the same idea: + +.. code-block:: c++ + + if consteval { + return slow_but_constexpr_path(); + } else { + return fast_runtime_path(); + } + +The check also recognizes the direct negated form and rewrites it to +``if !consteval``. + +The check only diagnoses direct uses in ``if`` statements whose condition is a +call to ``std::is_constant_evaluated()`` or a direct negation of that call. +Lookup through ``using`` declarations, ``using namespace`` directives, and +namespace aliases is handled. + +When the rewrite is source-safe, fix-its are provided. This includes inserting +braces around unbraced ``then`` and ``else`` branches because ``if consteval`` +always requires compound statements. + +The check still diagnoses, but does not provide fix-its for: + +- ``if`` statements with an init-statement +- ``if`` statements with a condition variable +- cases where the necessary edits would cross unsafe macro or source ranges + +``if constexpr`` and existing ``if consteval`` statements are ignored. diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-if-consteval.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-if-consteval.cpp new file mode 100644 index 0000000000000..95047d119b4f8 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-if-consteval.cpp @@ -0,0 +1,263 @@ +// RUN: %check_clang_tidy -std=c++23-or-later %s modernize-use-if-consteval %t + +namespace std { +constexpr bool is_constant_evaluated() noexcept { + return __builtin_is_constant_evaluated(); +} +} + +namespace mine { +constexpr bool is_constant_evaluated() noexcept { + return __builtin_is_constant_evaluated(); +} +} + +namespace alias = std; + +#define ICE_CALL() std::is_constant_evaluated() +#define IF_ICE_HEADER if (std::is_constant_evaluated()) +#define RETURN_ONE() return 1; +#define RETURN_THREE() return 3; + +bool runtime_predicate(); + +int direct() { + if (std::is_constant_evaluated()) + return 1; + else + return 2; + // CHECK-MESSAGES: :[[@LINE-4]]:7: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: if consteval { + // CHECK-FIXES-NEXT: return 1; + // CHECK-FIXES-NEXT: } else { + // CHECK-FIXES-NEXT: return 2; + // CHECK-FIXES-NEXT: } +} + +int direct_global() { + if (::std::is_constant_evaluated()) { + return 1; + } + return 2; + // CHECK-MESSAGES: :[[@LINE-4]]:7: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: if consteval { +} + +int using_decl() { + using std::is_constant_evaluated; + if (is_constant_evaluated()) { + return 1; + } + return 2; + // CHECK-MESSAGES: :[[@LINE-4]]:7: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: if consteval { +} + +int using_namespace() { + using namespace std; + if (is_constant_evaluated()) { + return 1; + } + return 2; + // CHECK-MESSAGES: :[[@LINE-4]]:7: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: if consteval { +} + +int namespace_alias() { + if (alias::is_constant_evaluated()) { + return 1; + } + return 2; + // CHECK-MESSAGES: :[[@LINE-4]]:7: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: if consteval { +} + +int negated() { + if (!std::is_constant_evaluated()) + return 1; + return 2; + // CHECK-MESSAGES: :[[@LINE-3]]:8: warning: use 'if !consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: if !consteval { + // CHECK-FIXES-NEXT: return 1; + // CHECK-FIXES-NEXT: } + // CHECK-FIXES-NEXT: return 2; +} + +int negated_alternative_token() { + if (not std::is_constant_evaluated()) + return 1; + return 2; + // CHECK-MESSAGES: :[[@LINE-3]]:11: warning: use 'if !consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: if !consteval { + // CHECK-FIXES-NEXT: return 1; + // CHECK-FIXES-NEXT: } + // CHECK-FIXES-NEXT: return 2; +} + +int extra_parens() { + if ((((std::is_constant_evaluated())))) { + return 1; + } + return 2; + // CHECK-MESSAGES: :[[@LINE-4]]:10: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: if consteval { +} + +template <typename T> +int templated() { + if (std::is_constant_evaluated()) { + return sizeof(T); + } + return 0; + // CHECK-MESSAGES: :[[@LINE-4]]:7: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: if consteval { +} + +template int templated<int>(); +template int templated<long>(); + +auto Lambda = [] { + if (std::is_constant_evaluated()) + return 1; + return 2; + // CHECK-MESSAGES: :[[@LINE-3]]:7: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: if consteval { + // CHECK-FIXES-NEXT: return 1; + // CHECK-FIXES-NEXT: } + // CHECK-FIXES-NEXT: return 2; +}; + +int else_if_chain(int Value) { + if (Value == 0) + return 0; + else if (std::is_constant_evaluated()) + return 1; + else + return 2; + // CHECK-MESSAGES: :[[@LINE-4]]:12: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: else if consteval { + // CHECK-FIXES-NEXT: return 1; + // CHECK-FIXES-NEXT: } else { + // CHECK-FIXES-NEXT: return 2; + // CHECK-FIXES-NEXT: } +} + +int macro_header_safe() { + if (ICE_CALL()) { + return 1; + } else { + return 2; + } + // CHECK-MESSAGES: :[[@LINE-5]]:7: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: if consteval { +} + +int with_init() { + if (int X = 0; std::is_constant_evaluated()) { + return X + 1; + } + return 0; + // CHECK-MESSAGES: :[[@LINE-4]]:18: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: if (int X = 0; std::is_constant_evaluated()) { +} + +int with_condition_variable() { + if (bool B = std::is_constant_evaluated()) + return B ? 1 : 2; + else + return 3; + // CHECK-MESSAGES: :[[@LINE-4]]:16: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: if (bool B = std::is_constant_evaluated()) + // CHECK-FIXES-NEXT: return B ? 1 : 2; + // CHECK-FIXES-NEXT: else + // CHECK-FIXES-NEXT: return 3; +} + +int macro_header_unsafe() { + IF_ICE_HEADER { + return 1; + } + return 0; + // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: IF_ICE_HEADER { +} + +int macro_body_unsafe() { + if (std::is_constant_evaluated()) + RETURN_ONE() + return 2; + // CHECK-MESSAGES: :[[@LINE-3]]:7: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: if (std::is_constant_evaluated()) + // CHECK-FIXES-NEXT: RETURN_ONE() + // CHECK-FIXES-NEXT: return 2; +} + +int macro_else_unsafe() { + if (std::is_constant_evaluated()) + return 1; + else + RETURN_THREE() + return 4; + // CHECK-MESSAGES: :[[@LINE-5]]:7: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: if consteval { + // CHECK-FIXES-NEXT: return 1; + // CHECK-FIXES-NEXT: } else + // CHECK-FIXES-NEXT: RETURN_THREE() + // CHECK-FIXES-NEXT: return 4; +} + +int not_std() { + if (mine::is_constant_evaluated()) { + return 1; + } + return 0; + // CHECK-MESSAGES-NOT: :[[@LINE-4]]:7: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] +} + +int composite_conditions() { + if (std::is_constant_evaluated() && runtime_predicate()) { + return 1; + } + if (!!std::is_constant_evaluated()) { + return 2; + } + return 0; + // CHECK-MESSAGES-NOT: :[[@LINE-7]]:7: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-MESSAGES-NOT: :[[@LINE-5]]:9: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] +} + +int if_constexpr() { + if constexpr (std::is_constant_evaluated()) { + return 1; + } + return 2; + // CHECK-MESSAGES-NOT: :[[@LINE-4]]:17: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] +} + +int already_if_consteval() { + if consteval { + // CHECK-MESSAGES-NOT: :[[@LINE-1]] warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + return 1; + } else { + return 2; + } +} + +int already_if_not_consteval() { + if !consteval { + // CHECK-MESSAGES-NOT: :[[@LINE-1]] warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + return 1; + } else { + return 2; + } +} + +template <typename T> +concept HasICE = requires { + std::is_constant_evaluated(); + // CHECK-MESSAGES-NOT: :[[@LINE-1]] warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] +}; + +using ICEPtr = decltype(std::is_constant_evaluated()) *; +ICEPtr Ptr = nullptr; +// CHECK-MESSAGES-NOT: :[[@LINE-2]] warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] >From 4d3af2820afaf4938eb6c16b4a8011f3a3e6e690 Mon Sep 17 00:00:00 2001 From: Daniil Dudkin <[email protected]> Date: Thu, 2 Apr 2026 20:04:36 +0300 Subject: [PATCH 2/2] Fix review comment --- .../modernize/UseIfConstevalCheck.cpp | 41 +++++++++++++------ .../checkers/modernize/use-if-consteval.cpp | 19 +++++++++ 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/clang-tools-extra/clang-tidy/modernize/UseIfConstevalCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseIfConstevalCheck.cpp index b31a4b97a01ff..859bda8294b79 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseIfConstevalCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseIfConstevalCheck.cpp @@ -10,6 +10,7 @@ #include "../utils/BracesAroundStatement.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Basic/CharInfo.h" #include "clang/Lex/Lexer.h" using namespace clang::ast_matchers; @@ -34,12 +35,12 @@ static const Stmt *ignoreAttributedStmt(const Stmt *S) { static std::optional<CharSourceRange> getHeaderRange(const IfStmt *If, const SourceManager &SM, const LangOptions &LangOpts) { - if (If->getIfLoc().isMacroID() || If->getRParenLoc().isMacroID()) + if (If->getLParenLoc().isMacroID() || If->getRParenLoc().isMacroID()) return std::nullopt; const CharSourceRange HeaderRange = Lexer::makeFileCharRange( - CharSourceRange::getTokenRange(If->getIfLoc(), If->getRParenLoc()), SM, - LangOpts); + CharSourceRange::getTokenRange(If->getLParenLoc(), If->getRParenLoc()), + SM, LangOpts); if (HeaderRange.isInvalid()) return std::nullopt; return HeaderRange; @@ -60,6 +61,14 @@ getBraceFix(const Stmt *S, const LangOptions &LangOpts, const SourceManager &SM, return BraceFix{true, Hints}; } + +static bool needsLeadingSpaceBeforeConsteval(SourceLocation LParenLoc, + const SourceManager &SM) { + bool Invalid = false; + const char *LParen = SM.getCharacterData(LParenLoc, &Invalid); + return Invalid || !isWhitespace(LParen[-1]); +} + void UseIfConstevalCheck::registerMatchers(MatchFinder *Finder) { const auto IsConstantEvaluatedCall = callExpr(callee(functionDecl(hasName("is_constant_evaluated"), @@ -88,16 +97,15 @@ void UseIfConstevalCheck::check(const MatchFinder::MatchResult &Result) { assert(If && Call && "expected to match an if statement and its call"); const bool IsNegated = Result.Nodes.getNodeAs<UnaryOperator>("negation"); - const char *Replacement = IsNegated ? "if !consteval" : "if consteval"; + const llvm::StringRef ConstevalClause = + IsNegated ? "!consteval" : "consteval"; const SourceLocation DiagLoc = Result.SourceManager->getExpansionLoc(Call->getExprLoc()); - auto Diag = - diag( - DiagLoc, - "use '%0' instead of checking 'std::is_constant_evaluated()' in this " - "'if' statement") - << Replacement; + auto Diag = diag(DiagLoc, "use 'if %0' instead of checking " + "'std::is_constant_evaluated()' in this " + "'if' statement") + << ConstevalClause; if (If->hasInitStorage() || If->hasVarStorage()) return; @@ -120,9 +128,16 @@ void UseIfConstevalCheck::check(const MatchFinder::MatchResult &Result) { *Result.SourceManager, If->getElseLoc()); } - std::string HeaderReplacement(Replacement); - if (ThenBraceFix->NeedsBraces) - HeaderReplacement += " {"; + const bool NeedsLeadingSpace = needsLeadingSpaceBeforeConsteval( + If->getLParenLoc(), *Result.SourceManager); + const std::string HeaderReplacement = [&] { + std::string Replacement = ConstevalClause.str(); + if (NeedsLeadingSpace) + Replacement.insert(0, 1, ' '); + if (ThenBraceFix->NeedsBraces) + Replacement += " {"; + return Replacement; + }(); Diag << FixItHint::CreateReplacement(*HeaderRange, HeaderReplacement); if (ThenBraceFix->NeedsBraces) diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-if-consteval.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-if-consteval.cpp index 95047d119b4f8..d3e9084776477 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-if-consteval.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-if-consteval.cpp @@ -16,6 +16,7 @@ namespace alias = std; #define ICE_CALL() std::is_constant_evaluated() #define IF_ICE_HEADER if (std::is_constant_evaluated()) +#define IF_ONLY if #define RETURN_ONE() return 1; #define RETURN_THREE() return 3; @@ -43,6 +44,15 @@ int direct_global() { // CHECK-FIXES: if consteval { } +int compact_spacing() { + if(std::is_constant_evaluated()) { + return 1; + } + return 2; + // CHECK-MESSAGES: :[[@LINE-4]]:6: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: if consteval { +} + int using_decl() { using std::is_constant_evaluated; if (is_constant_evaluated()) { @@ -182,6 +192,15 @@ int macro_header_unsafe() { // CHECK-FIXES: IF_ICE_HEADER { } +int macro_if_token_unsafe() { + IF_ONLY (std::is_constant_evaluated()) { + return 1; + } + return 0; + // CHECK-MESSAGES: :[[@LINE-4]]:12: warning: use 'if consteval' instead of checking 'std::is_constant_evaluated()' in this 'if' statement [modernize-use-if-consteval] + // CHECK-FIXES: IF_ONLY consteval { +} + int macro_body_unsafe() { if (std::is_constant_evaluated()) RETURN_ONE() _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
