Skip to content

Add clang tidy check performance constexpr non static in scope #147809

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/performance/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS

add_clang_library(clangTidyPerformanceModule STATIC
AvoidEndlCheck.cpp
ConstexprNonStaticInScopeCheck.cpp
EnumSizeCheck.cpp
FasterStringFindCheck.cpp
ForRangeCopyCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
@@ -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<clang::VarDecl>("constexprVar");
if (!Var)
return;
Comment on lines +30 to +31
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should write assert(Var);, "constexprVar" should always be valid.

const auto *EnclosingFunc = llvm::dyn_cast_or_null<clang::FunctionDecl>(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
}
Comment on lines +32 to +38
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic should be put directly inside ASTMatchers, try writing something like:

// previous code
unless(isStaticLocal()),
(!WarnInConstexprFuncCpp23 && !getLangOpts().CPlusPlus23) ? 
    unless(hasParent(functionDecl(isConstexpr()))) :
    anything() 


diag(Var->getLocation(),
"constexpr variable in function scope should be static to ensure static lifetime")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"static to ensure static lifetime" seems repetitive and not the main reason why the check exists.
You should write a message about performance.

<< FixItHint::CreateInsertion(Var->getSourceRange().getBegin(), "static ");
}

} // namespace clang::tidy::performance
Original file line number Diff line number Diff line change
@@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put here description as first sentence in docs

///
/// 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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return LangOpts.CPlusPlus;
return LangOpts.CPlusPlus11;

constexpr is only since C++11

}
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
private:
const bool WarnInConstexprFuncCpp23;
};

} // namespace clang::tidy::performance

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_CONSTEXPRNONSTATICINSCOPECHECK_H
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -36,6 +37,8 @@ class PerformanceModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<AvoidEndlCheck>("performance-avoid-endl");
CheckFactories.registerCheck<ConstexprNonStaticInScopeCheck>(
"performance-constexpr-non-static-in-scope");
CheckFactories.registerCheck<EnumSizeCheck>("performance-enum-size");
CheckFactories.registerCheck<FasterStringFindCheck>(
"performance-faster-string-find");
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
<clang-tidy/checks/performance/constexpr-non-static-in-scope>` check.

FIXME: Write a short description.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add description (should be same as first sentence in docs)


- New :doc:`portability-avoid-pragma-once
<clang-tidy/checks/portability/avoid-pragma-once>` check.

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ Clang-Tidy Checks
:doc:`openmp-exception-escape <openmp/exception-escape>`,
:doc:`openmp-use-default-none <openmp/use-default-none>`,
:doc:`performance-avoid-endl <performance/avoid-endl>`, "Yes"
:doc:`performance-constexpr-non-static-in-scope <performance/constexpr-non-static-in-scope>`, "Yes"
:doc:`performance-enum-size <performance/enum-size>`,
:doc:`performance-faster-string-find <performance/faster-string-find>`, "Yes"
:doc:`performance-for-range-copy <performance/for-range-copy>`, "Yes"
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please follow 80-character limit and dont use "the check" or its name.
Look at how other docs is written, e.g. https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-scoped-lock.html


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``:
Comment on lines +10 to +11
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
For example:
When a ``constexpr` ` is declared without ``static``:
For example when a ``constexpr`` is declared without ``static``:

.. code-block:: c++
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make a newline between text and code-block

// 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have name WarnInConstexprFuncBeforeCpp23 or WarnInConstexprFuncPreCpp23, WDYT?


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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please write about default value at the end: "Default is `false`"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.
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.

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// RUN: %check_clang_tidy %s performance-constexpr-non-static-in-scope %t -- -- -std=c++23
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Write 2 RUN commands in this test file. One for default value of option, another with false.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add tests with

  • template functions
  • consteval function/variables
  • template classes with template methods
  • variables that are type dependent on template arguments
  • multiple variables declared in one line constexpr int a = 10, b = 20;


// 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;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add newline

Loading