From 0eb7d53cfc48f2e9287bb116415620618ca850b7 Mon Sep 17 00:00:00 2001 From: Julian Schmidt <44101708+5chmidti@users.noreply.github.com> Date: Wed, 6 Dec 2023 18:56:05 +0100 Subject: [PATCH] [clang-tidy] add modernize-use-std-numbers (#66583) Finds constants and function calls to math functions that can be replaced with c++20's mathematical constants from the 'numbers' header and offers fix-it hints. Does not match the use of variables with that value, and instead, offers a replacement at the definition of those variables. --- .../clang-tidy/modernize/CMakeLists.txt | 1 + .../clang-tidy/modernize/ModernizeTidyModule.cpp | 3 + .../clang-tidy/modernize/UseStdNumbersCheck.cpp | 449 +++++++++++++++++++ .../clang-tidy/modernize/UseStdNumbersCheck.h | 49 ++ clang-tools-extra/docs/ReleaseNotes.rst | 7 + clang-tools-extra/docs/clang-tidy/checks/list.rst | 1 + .../checks/modernize/use-std-numbers.rst | 74 ++++ .../checkers/modernize/use-std-numbers.cpp | 493 +++++++++++++++++++++ 8 files changed, 1077 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-numbers.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-numbers.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index c40065358d2d..28ca52f46943 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 UseNullptrCheck.cpp UseOverrideCheck.cpp UseStartsEndsWithCheck.cpp + UseStdNumbersCheck.cpp UseStdPrintCheck.cpp UseTrailingReturnTypeCheck.cpp UseTransparentFunctorsCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index e994ffd2a75c..654f4bd0c6ba 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -40,6 +40,7 @@ #include "UseNullptrCheck.h" #include "UseOverrideCheck.h" #include "UseStartsEndsWithCheck.h" +#include "UseStdNumbersCheck.h" #include "UseStdPrintCheck.h" #include "UseTrailingReturnTypeCheck.h" #include "UseTransparentFunctorsCheck.h" @@ -69,6 +70,8 @@ public: CheckFactories.registerCheck("modernize-pass-by-value"); CheckFactories.registerCheck( "modernize-use-starts-ends-with"); + CheckFactories.registerCheck( + "modernize-use-std-numbers"); CheckFactories.registerCheck("modernize-use-std-print"); CheckFactories.registerCheck( "modernize-raw-string-literal"); diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp new file mode 100644 index 000000000000..1f2b034713ec --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp @@ -0,0 +1,449 @@ +//===--- UseStdNumbersCheck.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 "UseStdNumbersCheck.h" +#include "../ClangTidyDiagnosticConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchersInternal.h" +#include "clang/ASTMatchers/ASTMatchersMacros.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/MathExtras.h" +#include +#include +#include +#include +#include +#include +#include + +namespace { +using namespace clang::ast_matchers; +using clang::ast_matchers::internal::Matcher; +using llvm::StringRef; + +AST_MATCHER_P2(clang::FloatingLiteral, near, double, Value, double, + DiffThreshold) { + return std::abs(Node.getValueAsApproximateDouble() - Value) < DiffThreshold; +} + +AST_MATCHER_P(clang::QualType, hasCanonicalTypeUnqualified, + Matcher, InnerMatcher) { + return !Node.isNull() && + InnerMatcher.matches(Node->getCanonicalTypeUnqualified(), Finder, + Builder); +} + +AST_MATCHER(clang::QualType, isArithmetic) { + return !Node.isNull() && Node->isArithmeticType(); +} +AST_MATCHER(clang::QualType, isFloating) { + return !Node.isNull() && Node->isFloatingType(); +} + +AST_MATCHER_P(clang::Expr, anyOfExhaustive, + llvm::ArrayRef>, Exprs) { + bool FoundMatch = false; + for (const auto &InnerMatcher : Exprs) { + clang::ast_matchers::internal::BoundNodesTreeBuilder Result = *Builder; + if (InnerMatcher.matches(Node, Finder, &Result)) { + *Builder = std::move(Result); + FoundMatch = true; + } + } + return FoundMatch; +} + +// Using this struct to store the 'DiffThreshold' config value to create the +// matchers without the need to pass 'DiffThreshold' into every matcher. +// 'DiffThreshold' is needed in the 'near' matcher, which is used for matching +// the literal of every constant and for formulas' subexpressions that look at +// literals. +struct MatchBuilder { + auto + ignoreParenAndArithmeticCasting(const Matcher Matcher) const { + return expr(hasType(qualType(isArithmetic())), ignoringParenCasts(Matcher)); + } + + auto ignoreParenAndFloatingCasting(const Matcher Matcher) const { + return expr(hasType(qualType(isFloating())), ignoringParenCasts(Matcher)); + } + + auto matchMathCall(const StringRef FunctionName, + const Matcher ArgumentMatcher) const { + return expr(ignoreParenAndFloatingCasting( + callExpr(callee(functionDecl(hasName(FunctionName), + hasParameter(0, hasType(isArithmetic())))), + hasArgument(0, ArgumentMatcher)))); + } + + auto matchSqrt(const Matcher ArgumentMatcher) const { + return matchMathCall("sqrt", ArgumentMatcher); + } + + // Used for top-level matchers (i.e. the match that replaces Val with its + // constant). + // + // E.g. The matcher of `std::numbers::pi` uses this matcher to look for + // floatLiterals that have the value of pi. + // + // If the match is for a top-level match, we only care about the literal. + auto matchFloatLiteralNear(const StringRef Constant, const double Val) const { + return expr(ignoreParenAndFloatingCasting( + floatLiteral(near(Val, DiffThreshold)).bind(Constant))); + } + + // Used for non-top-level matchers (i.e. matchers that are used as inner + // matchers for top-level matchers). + // + // E.g.: The matcher of `std::numbers::log2e` uses this matcher to check if + // `e` of `log2(e)` is declared constant and initialized with the value for + // eulers number. + // + // Here, we do care about literals and about DeclRefExprs to variable + // declarations that are constant and initialized with `Val`. This allows + // top-level matchers to see through declared constants for their inner + // matches like the `std::numbers::log2e` matcher. + auto matchFloatValueNear(const double Val) const { + const auto Float = floatLiteral(near(Val, DiffThreshold)); + + const auto Dref = declRefExpr( + to(varDecl(hasType(qualType(isConstQualified(), isFloating())), + hasInitializer(ignoreParenAndFloatingCasting(Float))))); + return expr(ignoreParenAndFloatingCasting(anyOf(Float, Dref))); + } + + auto matchValue(const int64_t ValInt) const { + const auto Int = + expr(ignoreParenAndArithmeticCasting(integerLiteral(equals(ValInt)))); + const auto Float = expr(ignoreParenAndFloatingCasting( + matchFloatValueNear(static_cast(ValInt)))); + const auto Dref = declRefExpr(to(varDecl( + hasType(qualType(isConstQualified(), isArithmetic())), + hasInitializer(expr(anyOf(ignoringImplicit(Int), + ignoreParenAndFloatingCasting(Float))))))); + return expr(anyOf(Int, Float, Dref)); + } + + auto match1Div(const Matcher Match) const { + return binaryOperator(hasOperatorName("/"), hasLHS(matchValue(1)), + hasRHS(Match)); + } + + auto matchEuler() const { + return expr(anyOf(matchFloatValueNear(llvm::numbers::e), + matchMathCall("exp", matchValue(1)))); + } + auto matchEulerTopLevel() const { + return expr(anyOf(matchFloatLiteralNear("e_literal", llvm::numbers::e), + matchMathCall("exp", matchValue(1)).bind("e_pattern"))) + .bind("e"); + } + + auto matchLog2Euler() const { + return expr( + anyOf( + matchFloatLiteralNear("log2e_literal", llvm::numbers::log2e), + matchMathCall("log2", matchEuler()).bind("log2e_pattern"))) + .bind("log2e"); + } + + auto matchLog10Euler() const { + return expr( + anyOf( + matchFloatLiteralNear("log10e_literal", + llvm::numbers::log10e), + matchMathCall("log10", matchEuler()).bind("log10e_pattern"))) + .bind("log10e"); + } + + auto matchPi() const { return matchFloatValueNear(llvm::numbers::pi); } + auto matchPiTopLevel() const { + return matchFloatLiteralNear("pi_literal", llvm::numbers::pi).bind("pi"); + } + + auto matchEgamma() const { + return matchFloatLiteralNear("egamma_literal", llvm::numbers::egamma) + .bind("egamma"); + } + + auto matchInvPi() const { + return expr(anyOf(matchFloatLiteralNear("inv_pi_literal", + llvm::numbers::inv_pi), + match1Div(matchPi()).bind("inv_pi_pattern"))) + .bind("inv_pi"); + } + + auto matchInvSqrtPi() const { + return expr(anyOf( + matchFloatLiteralNear("inv_sqrtpi_literal", + llvm::numbers::inv_sqrtpi), + match1Div(matchSqrt(matchPi())).bind("inv_sqrtpi_pattern"))) + .bind("inv_sqrtpi"); + } + + auto matchLn2() const { + return expr(anyOf(matchFloatLiteralNear("ln2_literal", llvm::numbers::ln2), + matchMathCall("log", matchValue(2)).bind("ln2_pattern"))) + .bind("ln2"); + } + + auto machterLn10() const { + return expr( + anyOf(matchFloatLiteralNear("ln10_literal", llvm::numbers::ln10), + matchMathCall("log", matchValue(10)).bind("ln10_pattern"))) + .bind("ln10"); + } + + auto matchSqrt2() const { + return expr(anyOf(matchFloatLiteralNear("sqrt2_literal", + llvm::numbers::sqrt2), + matchSqrt(matchValue(2)).bind("sqrt2_pattern"))) + .bind("sqrt2"); + } + + auto matchSqrt3() const { + return expr(anyOf(matchFloatLiteralNear("sqrt3_literal", + llvm::numbers::sqrt3), + matchSqrt(matchValue(3)).bind("sqrt3_pattern"))) + .bind("sqrt3"); + } + + auto matchInvSqrt3() const { + return expr(anyOf(matchFloatLiteralNear("inv_sqrt3_literal", + llvm::numbers::inv_sqrt3), + match1Div(matchSqrt(matchValue(3))) + .bind("inv_sqrt3_pattern"))) + .bind("inv_sqrt3"); + } + + auto matchPhi() const { + const auto PhiFormula = binaryOperator( + hasOperatorName("/"), + hasLHS(binaryOperator( + hasOperatorName("+"), hasEitherOperand(matchValue(1)), + hasEitherOperand(matchMathCall("sqrt", matchValue(5))))), + hasRHS(matchValue(2))); + return expr(anyOf(PhiFormula.bind("phi_pattern"), + matchFloatLiteralNear("phi_literal", llvm::numbers::phi))) + .bind("phi"); + } + + double DiffThreshold; +}; + +std::string getCode(const StringRef Constant, const bool IsFloat, + const bool IsLongDouble) { + if (IsFloat) { + return ("std::numbers::" + Constant + "_v").str(); + } + if (IsLongDouble) { + return ("std::numbers::" + Constant + "_v").str(); + } + return ("std::numbers::" + Constant).str(); +} + +bool isRangeOfCompleteMacro(const clang::SourceRange &Range, + const clang::SourceManager &SM, + const clang::LangOptions &LO) { + if (!Range.getBegin().isMacroID()) { + return false; + } + if (!clang::Lexer::isAtStartOfMacroExpansion(Range.getBegin(), SM, LO)) { + return false; + } + + if (!Range.getEnd().isMacroID()) { + return false; + } + + if (!clang::Lexer::isAtEndOfMacroExpansion(Range.getEnd(), SM, LO)) { + return false; + } + + return true; +} + +} // namespace + +namespace clang::tidy::modernize { +UseStdNumbersCheck::UseStdNumbersCheck(const StringRef Name, + ClangTidyContext *const Context) + : ClangTidyCheck(Name, Context), + IncludeInserter(Options.getLocalOrGlobal("IncludeStyle", + utils::IncludeSorter::IS_LLVM), + areDiagsSelfContained()), + DiffThresholdString{Options.get("DiffThreshold", "0.001")} { + if (DiffThresholdString.getAsDouble(DiffThreshold)) { + configurationDiag( + "Invalid DiffThreshold config value: '%0', expected a double") + << DiffThresholdString; + DiffThreshold = 0.001; + } +} + +void UseStdNumbersCheck::registerMatchers(MatchFinder *const Finder) { + const auto Matches = MatchBuilder{DiffThreshold}; + static const auto ConstantMatchers = { + Matches.matchLog2Euler(), Matches.matchLog10Euler(), + Matches.matchEulerTopLevel(), Matches.matchEgamma(), + Matches.matchInvSqrtPi(), Matches.matchInvPi(), + Matches.matchPiTopLevel(), Matches.matchLn2(), + Matches.machterLn10(), Matches.matchSqrt2(), + Matches.matchInvSqrt3(), Matches.matchSqrt3(), + Matches.matchPhi(), + }; + + Finder->addMatcher( + expr( + anyOfExhaustive(ConstantMatchers), + unless(hasParent(explicitCastExpr(hasDestinationType(isFloating())))), + hasType(qualType(hasCanonicalTypeUnqualified( + anyOf(qualType(asString("float")).bind("float"), + qualType(asString("double")), + qualType(asString("long double")).bind("long double")))))), + this); +} + +void UseStdNumbersCheck::check(const MatchFinder::MatchResult &Result) { + /* + List of all math constants in the `` header + + e + + log2e + + log10e + + pi + + inv_pi + + inv_sqrtpi + + ln2 + + ln10 + + sqrt2 + + sqrt3 + + inv_sqrt3 + + egamma + + phi + */ + + // The ordering determines what constants are looked at first. + // E.g. look at 'inv_sqrt3' before 'sqrt3' to be able to replace the larger + // expression + constexpr auto Constants = std::array, 13>{ + std::pair{StringRef{"log2e"}, llvm::numbers::log2e}, + std::pair{StringRef{"log10e"}, llvm::numbers::log10e}, + std::pair{StringRef{"e"}, llvm::numbers::e}, + std::pair{StringRef{"egamma"}, llvm::numbers::egamma}, + std::pair{StringRef{"inv_sqrtpi"}, llvm::numbers::inv_sqrtpi}, + std::pair{StringRef{"inv_pi"}, llvm::numbers::inv_pi}, + std::pair{StringRef{"pi"}, llvm::numbers::pi}, + std::pair{StringRef{"ln2"}, llvm::numbers::ln2}, + std::pair{StringRef{"ln10"}, llvm::numbers::ln10}, + std::pair{StringRef{"sqrt2"}, llvm::numbers::sqrt2}, + std::pair{StringRef{"inv_sqrt3"}, llvm::numbers::inv_sqrt3}, + std::pair{StringRef{"sqrt3"}, llvm::numbers::sqrt3}, + std::pair{StringRef{"phi"}, llvm::numbers::phi}, + }; + + auto MatchedLiterals = + llvm::SmallVector>{}; + + const auto &SM = *Result.SourceManager; + const auto &LO = Result.Context->getLangOpts(); + + const auto IsFloat = Result.Nodes.getNodeAs("float") != nullptr; + const auto IsLongDouble = + Result.Nodes.getNodeAs("long double") != nullptr; + + for (const auto &[ConstantName, ConstantValue] : Constants) { + const auto *const Match = Result.Nodes.getNodeAs(ConstantName); + if (Match == nullptr) { + continue; + } + + const auto Range = Match->getSourceRange(); + + const auto IsMacro = Range.getBegin().isMacroID(); + + // We do not want to emit a diagnostic when we are matching a macro, but the + // match inside of the macro does not cover the whole macro. + if (IsMacro && !isRangeOfCompleteMacro(Range, SM, LO)) { + continue; + } + + if (const auto PatternBindString = (ConstantName + "_pattern").str(); + Result.Nodes.getNodeAs(PatternBindString) != nullptr) { + const auto Code = getCode(ConstantName, IsFloat, IsLongDouble); + diag(Range.getBegin(), "prefer '%0' to this %select{formula|macro}1") + << Code << IsMacro << FixItHint::CreateReplacement(Range, Code); + return; + } + + const auto LiteralBindString = (ConstantName + "_literal").str(); + if (const auto *const Literal = + Result.Nodes.getNodeAs(LiteralBindString)) { + MatchedLiterals.emplace_back( + ConstantName, + std::abs(Literal->getValueAsApproximateDouble() - ConstantValue), + Match); + } + } + + // We may have had no matches with literals, but a match with a pattern that + // was a part of a macro which was therefore skipped. + if (MatchedLiterals.empty()) { + return; + } + + llvm::sort(MatchedLiterals, [](const auto &LHS, const auto &RHS) { + return std::get<1>(LHS) < std::get<1>(RHS); + }); + + const auto &[Constant, Diff, Node] = MatchedLiterals.front(); + + const auto Range = Node->getSourceRange(); + const auto IsMacro = Range.getBegin().isMacroID(); + + // We do not want to emit a diagnostic when we are matching a macro, but the + // match inside of the macro does not cover the whole macro. + if (IsMacro && !isRangeOfCompleteMacro(Range, SM, LO)) { + return; + } + + const auto Code = getCode(Constant, IsFloat, IsLongDouble); + diag(Range.getBegin(), + "prefer '%0' to this %select{literal|macro}1, differs by '%2'") + << Code << IsMacro << llvm::formatv("{0:e2}", Diff).str() + << FixItHint::CreateReplacement(Range, Code) + << IncludeInserter.createIncludeInsertion( + Result.SourceManager->getFileID(Range.getBegin()), ""); +} + +void UseStdNumbersCheck::registerPPCallbacks( + const SourceManager &SM, Preprocessor *const PP, + Preprocessor *const ModuleExpanderPP) { + IncludeInserter.registerPreprocessor(PP); +} + +void UseStdNumbersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle()); + Options.store(Opts, "DiffThreshold", DiffThresholdString); +} +} // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.h b/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.h new file mode 100644 index 000000000000..05fc5ada14b8 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.h @@ -0,0 +1,49 @@ +//===--- UseStdNumbersCheck.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_USESTDNUMBERSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDNUMBERSCHECK_H + +#include "../ClangTidyCheck.h" +#include "../utils/IncludeInserter.h" + +namespace clang::tidy::modernize { + +/// Finds constants and function calls to math functions that can be replaced +/// with c++20's mathematical constants from the ``numbers`` header and +/// offers fix-it hints. +/// Does not match the use of variables with that value, and instead, +/// offers a replacement at the definition of those variables. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-numbers.html +class UseStdNumbersCheck : public ClangTidyCheck { +public: + UseStdNumbersCheck(StringRef Name, ClangTidyContext *Context); + + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus20; + } + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + std::optional getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } + +private: + utils::IncludeInserter IncludeInserter; + StringRef DiffThresholdString; + double DiffThreshold; +}; + +} // namespace clang::tidy::modernize + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDNUMBERSCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 4ff4494cef56..9262f9bbfe62 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -193,6 +193,13 @@ New checks replacing with ``starts_with`` when the method exists in the class. Notably, this will work with ``std::string`` and ``std::string_view``. +- New :doc:`modernize-use-std-numbers + ` check. + + Finds constants and function calls to math functions that can be replaced + with C++20's mathematical constants from the ``numbers`` header and + offers fix-it hints. + - New :doc:`performance-enum-size ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index df2d5d15238d..e6c02fe48fbf 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -293,6 +293,7 @@ Clang-Tidy Checks :doc:`modernize-use-nullptr `, "Yes" :doc:`modernize-use-override `, "Yes" :doc:`modernize-use-starts-ends-with `, "Yes" + :doc:`modernize-use-std-numbers `, "Yes" :doc:`modernize-use-std-print `, "Yes" :doc:`modernize-use-trailing-return-type `, "Yes" :doc:`modernize-use-transparent-functors `, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-numbers.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-numbers.rst new file mode 100644 index 000000000000..207e9c00e74b --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-numbers.rst @@ -0,0 +1,74 @@ +.. title:: clang-tidy - modernize-use-std-numbers + +modernize-use-std-numbers +========================= + +Finds constants and function calls to math functions that can be replaced +with C++20's mathematical constants from the ``numbers`` header and offers +fix-it hints. +Does not match the use of variables with that value, and instead, +offers a replacement for the definition of those variables. +Function calls that match the pattern of how the constant is calculated are +matched and replaced with the ``std::numbers`` constant. +The use of macros gets replaced with the corresponding ``std::numbers`` +constant, instead of changing the macro definition. + +The following list of constants from the ``numbers`` header are supported: + +* ``e`` +* ``log2e`` +* ``log10e`` +* ``pi`` +* ``inv_pi`` +* ``inv_sqrtpi`` +* ``ln2`` +* ``ln10`` +* ``sqrt2`` +* ``sqrt3`` +* ``inv_sqrt3`` +* ``egamma`` +* ``phi`` + +The list currently includes all constants as of C++20. + +The replacements use the type of the matched constant and can remove explicit +casts, i.e., switching between ``std::numbers::e``, +``std::numbers::e_v`` and ``std::numbers::e_v`` where +appropriate. + +.. code-block:: c++ + + double sqrt(double); + double log2(double); + void sink(auto&&) {} + void floatSink(float); + + #define MY_PI 3.1415926 + + void foo() { + const double Pi = 3.141592653589; // const double Pi = std::numbers::pi + const auto Use = Pi / 2; // no match for Pi + static constexpr double Euler = 2.7182818; // static constexpr double Euler = std::numbers::e; + + log2(exp(1)); // std::numbers::log2e; + log2(Euler); // std::numbers::log2e; + 1 / sqrt(MY_PI); // std::numbers::inv_sqrtpi; + sink(MY_PI); // sink(std::numbers::pi); + floatSink(MY_PI); // floatSink(std::numbers::pi); + floatSink(static_cast(MY_PI)); // floatSink(std::numbers::pi_v); + } + +Options +------- + +.. option:: DiffThreshold + + A floating point value that sets the detection threshold for when literals + match a constant. A literal matches a constant if + ``abs(literal - constant) < DiffThreshold`` evaluates to ``true``. Default + is `0.001`. + +.. option:: IncludeStyle + + A string specifying which include-style is used, `llvm` or `google`. Default + is `llvm`. diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-numbers.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-numbers.cpp new file mode 100644 index 000000000000..6c5762da5e2e --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-numbers.cpp @@ -0,0 +1,493 @@ +// RUN: %check_clang_tidy -check-suffix=ALL -std=c++20 %s modernize-use-std-numbers %t +// RUN: %check_clang_tidy -check-suffix=ALL,IMPRECISE -std=c++20 %s modernize-use-std-numbers %t -- -config="{CheckOptions: { modernize-use-std-numbers.DiffThreshold: 0.01 }}" + +// CHECK-FIXES-ALL: #include + +namespace bar { + double sqrt(double Arg); + float sqrt(float Arg); + template + auto sqrt(T val) { return sqrt(static_cast(val)); } + + static constexpr double e = 2.718281828459045235360287471352662497757247093; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:33: warning: prefer 'std::numbers::e' to this literal, differs by '0.00e+00' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double e = std::numbers::e; +} + +double exp(double Arg); +double log(double Arg); + +double log2(double Arg); +float log2(float Arg); +template +auto log2(T val) { return log2(static_cast(val)); } + +double log10(double Arg); + +template +void sink(T&&) { } + +void floatSink(float) {} + +#define MY_PI 3.1415926 + +#define INV_SQRT3 1 / bar::sqrt(3) +#define NOT_INV_SQRT3 1 / bar::sqrt(3) + 1 + +using my_double = double; +using my_float = float; + +void foo(){ + static constexpr double Pi = 3.1415926; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:34: warning: prefer 'std::numbers::pi' to this literal, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double Pi = std::numbers::pi; + + static constexpr double Euler = 2.7182818; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:37: warning: prefer 'std::numbers::e' to this literal, differs by '2.85e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double Euler = std::numbers::e; + + static constexpr double Phi = 1.6180339; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:35: warning: prefer 'std::numbers::phi' to this literal, differs by '8.87e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double Phi = std::numbers::phi; + + static constexpr double PiCopy = Pi; + static constexpr double PiDefineFromMacro = MY_PI; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:49: warning: prefer 'std::numbers::pi' to this macro, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double PiDefineFromMacro = std::numbers::pi; + + static constexpr double Pi2 = 3.14; + // CHECK-MESSAGES-IMPRECISE: :[[@LINE-1]]:35: warning: prefer 'std::numbers::pi' to this literal, differs by '1.59e-03' [modernize-use-std-numbers] + // CHECK-FIXES-IMPRECISE: static constexpr double Pi2 = std::numbers::pi; + static constexpr double Euler2 = 2.71; + // CHECK-MESSAGES-IMPRECISE: :[[@LINE-1]]:38: warning: prefer 'std::numbers::e' to this literal, differs by '8.28e-03' [modernize-use-std-numbers] + // CHECK-FIXES-IMPRECISE: static constexpr double Euler2 = std::numbers::e; + static constexpr double Phi2 = 1.61; + // CHECK-MESSAGES-IMPRECISE: :[[@LINE-1]]:36: warning: prefer 'std::numbers::phi' to this literal, differs by '8.03e-03' [modernize-use-std-numbers] + // CHECK-FIXES-IMPRECISE: static constexpr double Phi2 = std::numbers::phi; + + static constexpr double Pi3 = 3.1415926L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:35: warning: prefer 'std::numbers::pi_v' to this literal, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double Pi3 = std::numbers::pi_v; + + static constexpr double Euler3 = 2.7182818L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:38: warning: prefer 'std::numbers::e_v' to this literal, differs by '2.85e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double Euler3 = std::numbers::e_v; + + static constexpr double Phi3 = 1.6180339L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:36: warning: prefer 'std::numbers::phi_v' to this literal, differs by '8.87e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double Phi3 = std::numbers::phi_v; + + static constexpr long double Pi4 = 3.1415926L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:40: warning: prefer 'std::numbers::pi_v' to this literal, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr long double Pi4 = std::numbers::pi_v; + + static constexpr long double Euler4 = 2.7182818L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:43: warning: prefer 'std::numbers::e_v' to this literal, differs by '2.85e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr long double Euler4 = std::numbers::e_v; + + static constexpr long double Phi4 = 1.6180339L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:41: warning: prefer 'std::numbers::phi_v' to this literal, differs by '8.87e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr long double Phi4 = std::numbers::phi_v; + + static constexpr my_double Euler5 = 2.7182818; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:41: warning: prefer 'std::numbers::e' to this literal, differs by '2.85e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr my_double Euler5 = std::numbers::e; + + static constexpr my_float Euler6 = 2.7182818; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:40: warning: prefer 'std::numbers::e' to this literal, differs by '2.85e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr my_float Euler6 = std::numbers::e; + + static constexpr int NotEuler7 = 2.7182818; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:38: warning: prefer 'std::numbers::e' to this literal, differs by '2.85e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr int NotEuler7 = std::numbers::e; + + static constexpr double InvPi = 1.0 / Pi; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:37: warning: prefer 'std::numbers::inv_pi' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr double InvPi = std::numbers::inv_pi; + + static constexpr my_float Actually2MyFloat = 2; + bar::sqrt(Actually2MyFloat); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2_v; + + sink(MY_PI); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::pi' to this macro, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::pi); + + auto X = 42.0; + auto Y = X * 3.14; + // CHECK-MESSAGES-IMPRECISE: :[[@LINE-1]]:18: warning: prefer 'std::numbers::pi' to this literal, differs by '1.59e-03' [modernize-use-std-numbers] + // CHECK-FIXES-IMPRECISE: auto Y = X * std::numbers::pi; + + constexpr static auto One = 1; + constexpr static auto Two = 2; + + bar::sqrt(2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2; + + bar::sqrt(Two); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2; + + bar::sqrt(2.0); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2; + + auto Not2 = 2; + Not2 = 42; + bar::sqrt(Not2); + + const auto Actually2 = 2; + bar::sqrt(Actually2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2; + + exp(1); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::e; + + exp(One); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::e; + + exp(1.00000000000001); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::e; + + log2(exp(1)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log2e' to this formula [modernize-use-std-numbers] + // CHECK-MESSAGES-ALL: :[[@LINE-2]]:10: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log2e; + + log2(Euler); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log2e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log2e; + + log2(bar::e); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log2e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log2e; + + log2(Euler5); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log2e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log2e; + + log2(Euler6); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log2e_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log2e_v; + + log2(NotEuler7); + + auto log2e = 1.4426950; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:18: warning: prefer 'std::numbers::log2e' to this literal, differs by '4.09e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: auto log2e = std::numbers::log2e; + + floatSink(log2(Euler)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(std::numbers::log2e); + + floatSink(static_cast(log2(Euler))); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(std::numbers::log2e_v); + + floatSink(1.4426950); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e' to this literal, differs by '4.09e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(std::numbers::log2e); + + floatSink(static_cast(1.4426950)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e_v' to this literal, differs by '4.09e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(std::numbers::log2e_v); + + floatSink(log2(static_cast(Euler))); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(std::numbers::log2e_v); + + floatSink(static_cast(log2(static_cast(Euler)))); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(std::numbers::log2e_v); + + floatSink(static_cast(log2(static_cast(Euler)))); + + floatSink(static_cast(log2(static_cast(Euler)))); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:32: warning: prefer 'std::numbers::log2e_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(static_cast(std::numbers::log2e_v)); + + floatSink(1.4426950F); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e_v' to this literal, differs by '1.93e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(std::numbers::log2e_v); + + floatSink(static_cast(1.4426950F)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e' to this literal, differs by '1.93e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(std::numbers::log2e); + + floatSink(static_cast(1.4426950F)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:32: warning: prefer 'std::numbers::log2e_v' to this literal, differs by '1.93e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: floatSink(static_cast(std::numbers::log2e_v)); + + log10(exp(1)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log10e' to this formula [modernize-use-std-numbers] + // CHECK-MESSAGES-ALL: :[[@LINE-2]]:11: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log10e; + + log10(Euler); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log10e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log10e; + + log10(bar::e); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log10e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log10e; + + auto log10e = .434294; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:19: warning: prefer 'std::numbers::log10e' to this literal, differs by '4.82e-07' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: auto log10e = std::numbers::log10e; + + auto egamma = 0.5772156 * 42; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:19: warning: prefer 'std::numbers::egamma' to this literal, differs by '6.49e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: auto egamma = std::numbers::egamma * 42; + + sink(InvPi); + + sink(1 / Pi); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::inv_pi' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::inv_pi); + + sink(1 / bar::sqrt(Pi)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::inv_sqrtpi' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::inv_sqrtpi); + + sink(1 / bar::sqrt(MY_PI)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::inv_sqrtpi' to this formula [modernize-use-std-numbers] + // CHECK-MESSAGES-ALL: :[[@LINE-2]]:24: warning: prefer 'std::numbers::pi' to this macro, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::inv_sqrtpi); + + log(2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::ln2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::ln2; + + log(10); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::ln10' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::ln10; + + bar::sqrt(2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2; + + sink(1 / bar::sqrt(3)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::inv_sqrt3' to this formula [modernize-use-std-numbers] + // CHECK-MESSAGES-ALL: :[[@LINE-2]]:14: warning: prefer 'std::numbers::sqrt3' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::inv_sqrt3); + + sink(INV_SQRT3); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::inv_sqrt3' to this macro [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::inv_sqrt3); + + sink(NOT_INV_SQRT3); + + const auto inv_sqrt3f = .577350269F; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:29: warning: prefer 'std::numbers::inv_sqrt3_v' to this literal, differs by '1.04e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: const auto inv_sqrt3f = std::numbers::inv_sqrt3_v; + + bar::sqrt(3); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt3' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt3; + + auto somePhi = 1.6180339; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:20: warning: prefer 'std::numbers::phi' to this literal, differs by '8.87e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: auto somePhi = std::numbers::phi; + + sink(Phi); + + sink((42 + bar::sqrt(5)) / 2); + + sink((1 + bar::sqrt(5)) / 2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::phi' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::phi); + + sink((bar::sqrt(5.0F) + 1) / 2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::phi_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::phi_v); +} + + + +template +void baz(){ + static constexpr T Pi = 3.1415926; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:29: warning: prefer 'std::numbers::pi' to this literal, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr T Pi = std::numbers::pi; + + static constexpr T Euler = 2.7182818; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:32: warning: prefer 'std::numbers::e' to this literal, differs by '2.85e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr T Euler = std::numbers::e; + + static constexpr T Phi = 1.6180339; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:30: warning: prefer 'std::numbers::phi' to this literal, differs by '8.87e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr T Phi = std::numbers::phi; + + static constexpr T PiCopy = Pi; + static constexpr T PiDefineFromMacro = MY_PI; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:44: warning: prefer 'std::numbers::pi' to this macro, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr T PiDefineFromMacro = std::numbers::pi; + + static constexpr T Pi2 = 3.14; + // CHECK-MESSAGES-IMPRECISE: :[[@LINE-1]]:30: warning: prefer 'std::numbers::pi' to this literal, differs by '1.59e-03' [modernize-use-std-numbers] + // CHECK-FIXES-IMPRECISE: static constexpr T Pi2 = std::numbers::pi; + static constexpr T Euler2 = 2.71; + // CHECK-MESSAGES-IMPRECISE: :[[@LINE-1]]:33: warning: prefer 'std::numbers::e' to this literal, differs by '8.28e-03' [modernize-use-std-numbers] + // CHECK-FIXES-IMPRECISE: static constexpr T Euler2 = std::numbers::e; + static constexpr T Phi2 = 1.61; + // CHECK-MESSAGES-IMPRECISE: :[[@LINE-1]]:31: warning: prefer 'std::numbers::phi' to this literal, differs by '8.03e-03' [modernize-use-std-numbers] + // CHECK-FIXES-IMPRECISE: static constexpr T Phi2 = std::numbers::phi; + + static constexpr T Pi3 = 3.1415926L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:30: warning: prefer 'std::numbers::pi_v' to this literal, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr T Pi3 = std::numbers::pi_v; + + static constexpr T Euler3 = 2.7182818L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:33: warning: prefer 'std::numbers::e_v' to this literal, differs by '2.85e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr T Euler3 = std::numbers::e_v; + + static constexpr T Phi3 = 1.6180339L; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:31: warning: prefer 'std::numbers::phi_v' to this literal, differs by '8.87e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: static constexpr T Phi3 = std::numbers::phi_v; + + static constexpr my_float Actually2MyFloat = 2; + bar::sqrt(Actually2MyFloat); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2_v; + + constexpr static T One = 1; + constexpr static T Two = 2; + + bar::sqrt(2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2; + + bar::sqrt(Two); + + bar::sqrt(2.0); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2; + + T Not2 = 2; + Not2 = 42; + bar::sqrt(Not2); + + const T Actually2 = 2; + bar::sqrt(Actually2); + + exp(1); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::e; + + exp(One); + + exp(1.00000000000001); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::e; + + log2(exp(1)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log2e' to this formula [modernize-use-std-numbers] + // CHECK-MESSAGES-ALL: :[[@LINE-2]]:10: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log2e; + + log2(Euler); + + log2(bar::e); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log2e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log2e; + + T log2e = 1.4426950; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:15: warning: prefer 'std::numbers::log2e' to this literal, differs by '4.09e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: T log2e = std::numbers::log2e; + + log10(exp(1)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log10e' to this formula [modernize-use-std-numbers] + // CHECK-MESSAGES-ALL: :[[@LINE-2]]:11: warning: prefer 'std::numbers::e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log10e; + + log10(Euler); + + log10(bar::e); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::log10e' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::log10e; + + T log10e = .434294; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:16: warning: prefer 'std::numbers::log10e' to this literal, differs by '4.82e-07' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: T log10e = std::numbers::log10e; + + T egamma = 0.5772156 * 42; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:16: warning: prefer 'std::numbers::egamma' to this literal, differs by '6.49e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: T egamma = std::numbers::egamma * 42; + + sink(1 / Pi); + + sink(1 / bar::sqrt(Pi)); + + sink(1 / bar::sqrt(MY_PI)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::inv_sqrtpi' to this formula [modernize-use-std-numbers] + // CHECK-MESSAGES-ALL: :[[@LINE-2]]:24: warning: prefer 'std::numbers::pi' to this macro, differs by '5.36e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::inv_sqrtpi); + + + log(2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::ln2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::ln2; + + log(10); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::ln10' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::ln10; + + bar::sqrt(2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt2' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt2; + + sink(1 / bar::sqrt(3)); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::inv_sqrt3' to this formula [modernize-use-std-numbers] + // CHECK-MESSAGES-ALL: :[[@LINE-2]]:14: warning: prefer 'std::numbers::sqrt3' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::inv_sqrt3); + + bar::sqrt(3); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:5: warning: prefer 'std::numbers::sqrt3' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: std::numbers::sqrt3; + + T phi = 1.6180339; + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:13: warning: prefer 'std::numbers::phi' to this literal, differs by '8.87e-08' [modernize-use-std-numbers] + // CHECK-FIXES-ALL: T phi = std::numbers::phi; + + sink((42 + bar::sqrt(5)) / 2); + + sink((1 + bar::sqrt(5)) / 2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::phi' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::phi); + + sink((bar::sqrt(5.0F) + 1) / 2); + // CHECK-MESSAGES-ALL: :[[@LINE-1]]:10: warning: prefer 'std::numbers::phi_v' to this formula [modernize-use-std-numbers] + // CHECK-FIXES-ALL: sink(std::numbers::phi_v); +} + +template +void foobar(){ + const T Two = 2; + bar::sqrt(Two); +} +void use_templates() { + foobar(); + foobar(); + + baz(); + baz(); +} + +#define BIG_MARCO \ + struct InvSqrt3 { \ + template static T get() { return 1 / bar::sqrt(3); } \ + } + +BIG_MARCO; + +void use_BIG_MACRO() { +InvSqrt3 f{}; +f.get(); +f.get(); +} -- 2.11.4.GIT