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..4cc1d5efbad06 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/constexpr-non-static-in-scope.cpp @@ -0,0 +1,31 @@ +// RUN: %check_clang_tidy %s performance-constexpr-non-static-in-scope %t -- -- -std=c++23 + +// 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; +} + +// 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; +} + +// 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