From 1cf55c155ce9bf6fe79a7329ea5622d3cbf7b970 Mon Sep 17 00:00:00 2001 From: Spaarsh Date: Thu, 10 Jul 2025 00:47:22 +0530 Subject: [PATCH 1/2] [ADDED] clang-tidy check performance-constexpr-non-static-in-scope --- .../clang-tidy/performance/CMakeLists.txt | 1 + .../ConstexprNonStaticInScopeCheck.cpp | 45 +++++++++++++++++++ .../ConstexprNonStaticInScopeCheck.h | 37 +++++++++++++++ .../performance/PerformanceTidyModule.cpp | 3 ++ clang-tools-extra/docs/ReleaseNotes.rst | 5 +++ .../docs/clang-tidy/checks/list.rst | 1 + .../constexpr-non-static-in-scope.rst | 42 +++++++++++++++++ .../constexpr-non-static-in-scope.cpp | 14 ++++++ 8 files changed, 148 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/performance/ConstexprNonStaticInScopeCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/performance/ConstexprNonStaticInScopeCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/performance/constexpr-non-static-in-scope.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/performance/constexpr-non-static-in-scope.cpp diff --git a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt index c6e547c5089fb..59a7bf9e1e08c 100644 --- a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangTidyPerformanceModule STATIC AvoidEndlCheck.cpp + ConstexprNonStaticInScopeCheck.cpp EnumSizeCheck.cpp FasterStringFindCheck.cpp ForRangeCopyCheck.cpp diff --git a/clang-tools-extra/clang-tidy/performance/ConstexprNonStaticInScopeCheck.cpp b/clang-tools-extra/clang-tidy/performance/ConstexprNonStaticInScopeCheck.cpp new file mode 100644 index 0000000000000..fa4ac9de77b0c --- /dev/null +++ b/clang-tools-extra/clang-tidy/performance/ConstexprNonStaticInScopeCheck.cpp @@ -0,0 +1,45 @@ +//===--- ConstexprNonStaticInScopeCheck.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 "ConstexprNonStaticInScopeCheck.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::performance { + +void ConstexprNonStaticInScopeCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(varDecl( + hasLocalStorage(), + isConstexpr(), + unless(isStaticLocal()) + ).bind("constexprVar"), this); +} + +void ConstexprNonStaticInScopeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "WarnInConstexprFuncCpp23", WarnInConstexprFuncCpp23); +} + +void ConstexprNonStaticInScopeCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Var = Result.Nodes.getNodeAs("constexprVar"); + if (!Var) + return; + const auto *EnclosingFunc = llvm::dyn_cast_or_null(Var->getDeclContext()); + + if (EnclosingFunc && EnclosingFunc->isConstexpr()) { + // If the function is constexpr, only warn in C++23 and above + if (!Result.Context->getLangOpts().CPlusPlus23 || !WarnInConstexprFuncCpp23) + return; // Don't warn unless in C++23+ AND the option is enabled + } + + diag(Var->getLocation(), + "constexpr variable in function scope should be static to ensure static lifetime") + << FixItHint::CreateInsertion(Var->getSourceRange().getBegin(), "static "); +} + +} // namespace clang::tidy::performance diff --git a/clang-tools-extra/clang-tidy/performance/ConstexprNonStaticInScopeCheck.h b/clang-tools-extra/clang-tidy/performance/ConstexprNonStaticInScopeCheck.h new file mode 100644 index 0000000000000..8bc353519a613 --- /dev/null +++ b/clang-tools-extra/clang-tidy/performance/ConstexprNonStaticInScopeCheck.h @@ -0,0 +1,37 @@ +//===--- ConstexprNonStaticInScopeCheck.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_PERFORMANCE_CONSTEXPRNONSTATICINSCOPECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_CONSTEXPRNONSTATICINSCOPECHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::performance { + +/// FIXME: Write a short description. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/performance/constexpr-non-static-in-scope.html +class ConstexprNonStaticInScopeCheck : public ClangTidyCheck { +public: + ConstexprNonStaticInScopeCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + WarnInConstexprFuncCpp23(Options.get("WarnInConstexprFuncCpp23", /*Default=*/true)) {} + 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.CPlusPlus; + } + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; +private: + const bool WarnInConstexprFuncCpp23; +}; + +} // namespace clang::tidy::performance + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_CONSTEXPRNONSTATICINSCOPECHECK_H diff --git a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp index 10ad9ec6fef4c..b571b9b5dd158 100644 --- a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp @@ -10,6 +10,7 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" #include "AvoidEndlCheck.h" +#include "ConstexprNonStaticInScopeCheck.h" #include "EnumSizeCheck.h" #include "FasterStringFindCheck.h" #include "ForRangeCopyCheck.h" @@ -36,6 +37,8 @@ class PerformanceModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck("performance-avoid-endl"); + CheckFactories.registerCheck( + "performance-constexpr-non-static-in-scope"); CheckFactories.registerCheck("performance-enum-size"); CheckFactories.registerCheck( "performance-faster-string-find"); diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index e021d6350694e..a814597b130f0 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -148,6 +148,11 @@ New checks Finds uses of ``std::lock_guard`` and suggests replacing them with C++17's alternative ``std::scoped_lock``. +- New :doc:`performance-constexpr-non-static-in-scope + ` check. + + FIXME: Write a short description. + - New :doc:`portability-avoid-pragma-once ` 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..2b2a9a316e6d9 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -335,6 +335,7 @@ Clang-Tidy Checks :doc:`openmp-exception-escape `, :doc:`openmp-use-default-none `, :doc:`performance-avoid-endl `, "Yes" + :doc:`performance-constexpr-non-static-in-scope `, "Yes" :doc:`performance-enum-size `, :doc:`performance-faster-string-find `, "Yes" :doc:`performance-for-range-copy `, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/performance/constexpr-non-static-in-scope.rst b/clang-tools-extra/docs/clang-tidy/checks/performance/constexpr-non-static-in-scope.rst new file mode 100644 index 0000000000000..f76db268648da --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/performance/constexpr-non-static-in-scope.rst @@ -0,0 +1,42 @@ +.. title:: clang-tidy - performance-constexpr-non-static-in-scope + +performance-constexpr-non-static-in-scope +========================================= + +The check ``performance-constexpr-non-static-in-scope`` identifies ``constexpr`` variables declared in function (local) scope that are not marked ``static``. In most cases, such variables should be declared `static constexpr` to avoid creating a new instance on every function call. + +The check will always warn for non-static ``constexpr`` variables in non-constexpr functions. For ``constexpr`` functions, the check warns only in C++23 and newer (and this behavior can be controlled with the ``WarnInConstexprFuncCpp23`` option). + +For example: +When a ``constexpr` ` is declared without ``static``: +.. code-block:: c++ + // BEFORE + void foo() { + constexpr int x = 42; + } + + // AFTER + void foo() { + static constexpr int x = 42; // Corrected to static constexpr + } + +When a ``constexpr`` is declared in a ``constexpr`` function without ``static`` (only in C++23 and newer): +.. code-block:: c++ + // BEFORE + constexpr int bar() { + constexpr int y = 123; + return y; + } + + // AFTER + constexpr int bar() { + static constexpr int y = 123; // Corrected to static constexpr + return y; + } + +Options +------- + +.. option:: WarnInConstexprFuncCpp23 + + When true (default), warns on ``constexpr`` variables inside ``constexpr`` functions in C++23 and newer. Set to ``false`` to disable this warning in that scenario. diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/constexpr-non-static-in-scope.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/constexpr-non-static-in-scope.cpp new file mode 100644 index 0000000000000..0220cddc53c25 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/constexpr-non-static-in-scope.cpp @@ -0,0 +1,14 @@ +// RUN: %check_clang_tidy %s performance-constexpr-non-static-in-scope %t + +// FIXME: Add something that triggers the check here. +void f(); +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [performance-constexpr-non-static-in-scope] + +// FIXME: Verify the applied fix. +// * Make the CHECK patterns specific enough and try to make verified lines +// unique to avoid incorrect matches. +// * Use {{}} for regular expressions. +// CHECK-FIXES: {{^}}void awesome_f();{{$}} + +// FIXME: Add something that doesn't trigger the check here. +void awesome_f2(); From 1850a9964c7d1213e395daaa5e522c8a56cc3908 Mon Sep 17 00:00:00 2001 From: Spaarsh Date: Thu, 10 Jul 2025 01:02:00 +0530 Subject: [PATCH 2/2] [ADDED] Tests --- .../constexpr-non-static-in-scope.cpp | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/constexpr-non-static-in-scope.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/constexpr-non-static-in-scope.cpp index 0220cddc53c25..4cc1d5efbad06 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/performance/constexpr-non-static-in-scope.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/constexpr-non-static-in-scope.cpp @@ -1,14 +1,31 @@ -// RUN: %check_clang_tidy %s performance-constexpr-non-static-in-scope %t +// RUN: %check_clang_tidy %s performance-constexpr-non-static-in-scope %t -- -- -std=c++23 -// FIXME: Add something that triggers the check here. -void f(); -// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [performance-constexpr-non-static-in-scope] +// This should trigger the check (constexpr local, not static). +void f() { + constexpr int x = 42; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constexpr variable in function scope should be static to ensure static lifetime [performance-constexpr-non-static-in-scope] + // CHECK-FIXES: static constexpr int x = 42; +} -// FIXME: Verify the applied fix. -// * Make the CHECK patterns specific enough and try to make verified lines -// unique to avoid incorrect matches. -// * Use {{}} for regular expressions. -// CHECK-FIXES: {{^}}void awesome_f();{{$}} +// This should trigger if WarnInConstexprFuncCpp23 is true and C++23 or newer. +constexpr int g() { + constexpr int y = 123; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constexpr variable in function scope should be static to ensure static lifetime [performance-constexpr-non-static-in-scope] + // CHECK-FIXES: static constexpr int y = 123; + return y; +} -// FIXME: Add something that doesn't trigger the check here. -void awesome_f2(); +// This should NOT trigger the check (already static). +void h() { + static constexpr int z = 99; +} + +// This should NOT trigger the check (not constexpr). +void i() { + int w = 100; +} + +// This should NOT trigger the check (not declared inside a function) +namespace ns { + inline constexpr int MAX_SIZE = 1024; +} \ No newline at end of file