xref: /llvm-project/clang-tools-extra/clang-tidy/modernize/UseStdNumbersCheck.cpp (revision a5f54175dcf120180c3d91bbc13062bbf8f42f61)
10eb7d53cSJulian Schmidt //===--- UseStdNumbersCheck.cpp - clang_tidy ------------------------------===//
20eb7d53cSJulian Schmidt //
30eb7d53cSJulian Schmidt // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40eb7d53cSJulian Schmidt // See https://llvm.org/LICENSE.txt for license information.
50eb7d53cSJulian Schmidt // SPDX_License_Identifier: Apache_2.0 WITH LLVM_exception
60eb7d53cSJulian Schmidt //
70eb7d53cSJulian Schmidt //===----------------------------------------------------------------------===//
80eb7d53cSJulian Schmidt 
90eb7d53cSJulian Schmidt #include "UseStdNumbersCheck.h"
100eb7d53cSJulian Schmidt #include "../ClangTidyDiagnosticConsumer.h"
110eb7d53cSJulian Schmidt #include "clang/AST/ASTContext.h"
120eb7d53cSJulian Schmidt #include "clang/AST/Decl.h"
130eb7d53cSJulian Schmidt #include "clang/AST/Expr.h"
140eb7d53cSJulian Schmidt #include "clang/AST/Stmt.h"
150eb7d53cSJulian Schmidt #include "clang/AST/Type.h"
160eb7d53cSJulian Schmidt #include "clang/ASTMatchers/ASTMatchFinder.h"
170eb7d53cSJulian Schmidt #include "clang/ASTMatchers/ASTMatchers.h"
180eb7d53cSJulian Schmidt #include "clang/ASTMatchers/ASTMatchersInternal.h"
190eb7d53cSJulian Schmidt #include "clang/ASTMatchers/ASTMatchersMacros.h"
200eb7d53cSJulian Schmidt #include "clang/Basic/Diagnostic.h"
210eb7d53cSJulian Schmidt #include "clang/Basic/LLVM.h"
220eb7d53cSJulian Schmidt #include "clang/Basic/LangOptions.h"
230eb7d53cSJulian Schmidt #include "clang/Basic/SourceLocation.h"
240eb7d53cSJulian Schmidt #include "clang/Basic/SourceManager.h"
250eb7d53cSJulian Schmidt #include "clang/Lex/Lexer.h"
260eb7d53cSJulian Schmidt #include "llvm/ADT/STLExtras.h"
270eb7d53cSJulian Schmidt #include "llvm/ADT/SmallVector.h"
280eb7d53cSJulian Schmidt #include "llvm/ADT/StringRef.h"
290eb7d53cSJulian Schmidt #include "llvm/Support/FormatVariadic.h"
300eb7d53cSJulian Schmidt #include "llvm/Support/MathExtras.h"
310eb7d53cSJulian Schmidt #include <array>
32*a5f54175SZentrik #include <cmath>
330eb7d53cSJulian Schmidt #include <cstdint>
340eb7d53cSJulian Schmidt #include <cstdlib>
350eb7d53cSJulian Schmidt #include <initializer_list>
360eb7d53cSJulian Schmidt #include <string>
370eb7d53cSJulian Schmidt #include <tuple>
380eb7d53cSJulian Schmidt #include <utility>
390eb7d53cSJulian Schmidt 
400eb7d53cSJulian Schmidt namespace {
410eb7d53cSJulian Schmidt using namespace clang::ast_matchers;
420eb7d53cSJulian Schmidt using clang::ast_matchers::internal::Matcher;
430eb7d53cSJulian Schmidt using llvm::StringRef;
440eb7d53cSJulian Schmidt 
AST_MATCHER_P2(clang::FloatingLiteral,near,double,Value,double,DiffThreshold)450eb7d53cSJulian Schmidt AST_MATCHER_P2(clang::FloatingLiteral, near, double, Value, double,
460eb7d53cSJulian Schmidt                DiffThreshold) {
470eb7d53cSJulian Schmidt   return std::abs(Node.getValueAsApproximateDouble() - Value) < DiffThreshold;
480eb7d53cSJulian Schmidt }
490eb7d53cSJulian Schmidt 
AST_MATCHER_P(clang::QualType,hasCanonicalTypeUnqualified,Matcher<clang::QualType>,InnerMatcher)500eb7d53cSJulian Schmidt AST_MATCHER_P(clang::QualType, hasCanonicalTypeUnqualified,
510eb7d53cSJulian Schmidt               Matcher<clang::QualType>, InnerMatcher) {
520eb7d53cSJulian Schmidt   return !Node.isNull() &&
530eb7d53cSJulian Schmidt          InnerMatcher.matches(Node->getCanonicalTypeUnqualified(), Finder,
540eb7d53cSJulian Schmidt                               Builder);
550eb7d53cSJulian Schmidt }
560eb7d53cSJulian Schmidt 
AST_MATCHER(clang::QualType,isArithmetic)570eb7d53cSJulian Schmidt AST_MATCHER(clang::QualType, isArithmetic) {
580eb7d53cSJulian Schmidt   return !Node.isNull() && Node->isArithmeticType();
590eb7d53cSJulian Schmidt }
AST_MATCHER(clang::QualType,isFloating)600eb7d53cSJulian Schmidt AST_MATCHER(clang::QualType, isFloating) {
610eb7d53cSJulian Schmidt   return !Node.isNull() && Node->isFloatingType();
620eb7d53cSJulian Schmidt }
630eb7d53cSJulian Schmidt 
AST_MATCHER_P(clang::Expr,anyOfExhaustive,std::vector<Matcher<clang::Stmt>>,Exprs)64e1fa2feaSPiotr Zegar AST_MATCHER_P(clang::Expr, anyOfExhaustive, std::vector<Matcher<clang::Stmt>>,
65e1fa2feaSPiotr Zegar               Exprs) {
660eb7d53cSJulian Schmidt   bool FoundMatch = false;
670eb7d53cSJulian Schmidt   for (const auto &InnerMatcher : Exprs) {
680eb7d53cSJulian Schmidt     clang::ast_matchers::internal::BoundNodesTreeBuilder Result = *Builder;
690eb7d53cSJulian Schmidt     if (InnerMatcher.matches(Node, Finder, &Result)) {
700eb7d53cSJulian Schmidt       *Builder = std::move(Result);
710eb7d53cSJulian Schmidt       FoundMatch = true;
720eb7d53cSJulian Schmidt     }
730eb7d53cSJulian Schmidt   }
740eb7d53cSJulian Schmidt   return FoundMatch;
750eb7d53cSJulian Schmidt }
760eb7d53cSJulian Schmidt 
770eb7d53cSJulian Schmidt // Using this struct to store the 'DiffThreshold' config value to create the
780eb7d53cSJulian Schmidt // matchers without the need to pass 'DiffThreshold' into every matcher.
790eb7d53cSJulian Schmidt // 'DiffThreshold' is needed in the 'near' matcher, which is used for matching
800eb7d53cSJulian Schmidt // the literal of every constant and for formulas' subexpressions that look at
810eb7d53cSJulian Schmidt // literals.
820eb7d53cSJulian Schmidt struct MatchBuilder {
830eb7d53cSJulian Schmidt   auto
ignoreParenAndArithmeticCasting__anon60bcb47f0111::MatchBuilder840eb7d53cSJulian Schmidt   ignoreParenAndArithmeticCasting(const Matcher<clang::Expr> Matcher) const {
850eb7d53cSJulian Schmidt     return expr(hasType(qualType(isArithmetic())), ignoringParenCasts(Matcher));
860eb7d53cSJulian Schmidt   }
870eb7d53cSJulian Schmidt 
ignoreParenAndFloatingCasting__anon60bcb47f0111::MatchBuilder880eb7d53cSJulian Schmidt   auto ignoreParenAndFloatingCasting(const Matcher<clang::Expr> Matcher) const {
890eb7d53cSJulian Schmidt     return expr(hasType(qualType(isFloating())), ignoringParenCasts(Matcher));
900eb7d53cSJulian Schmidt   }
910eb7d53cSJulian Schmidt 
matchMathCall__anon60bcb47f0111::MatchBuilder920eb7d53cSJulian Schmidt   auto matchMathCall(const StringRef FunctionName,
930eb7d53cSJulian Schmidt                      const Matcher<clang::Expr> ArgumentMatcher) const {
940eb7d53cSJulian Schmidt     return expr(ignoreParenAndFloatingCasting(
950eb7d53cSJulian Schmidt         callExpr(callee(functionDecl(hasName(FunctionName),
960eb7d53cSJulian Schmidt                                      hasParameter(0, hasType(isArithmetic())))),
970eb7d53cSJulian Schmidt                  hasArgument(0, ArgumentMatcher))));
980eb7d53cSJulian Schmidt   }
990eb7d53cSJulian Schmidt 
matchSqrt__anon60bcb47f0111::MatchBuilder1000eb7d53cSJulian Schmidt   auto matchSqrt(const Matcher<clang::Expr> ArgumentMatcher) const {
1010eb7d53cSJulian Schmidt     return matchMathCall("sqrt", ArgumentMatcher);
1020eb7d53cSJulian Schmidt   }
1030eb7d53cSJulian Schmidt 
1040eb7d53cSJulian Schmidt   // Used for top-level matchers (i.e. the match that replaces Val with its
1050eb7d53cSJulian Schmidt   // constant).
1060eb7d53cSJulian Schmidt   //
1070eb7d53cSJulian Schmidt   // E.g. The matcher of `std::numbers::pi` uses this matcher to look for
1080eb7d53cSJulian Schmidt   // floatLiterals that have the value of pi.
1090eb7d53cSJulian Schmidt   //
1100eb7d53cSJulian Schmidt   // If the match is for a top-level match, we only care about the literal.
matchFloatLiteralNear__anon60bcb47f0111::MatchBuilder1110eb7d53cSJulian Schmidt   auto matchFloatLiteralNear(const StringRef Constant, const double Val) const {
1120eb7d53cSJulian Schmidt     return expr(ignoreParenAndFloatingCasting(
1130eb7d53cSJulian Schmidt         floatLiteral(near(Val, DiffThreshold)).bind(Constant)));
1140eb7d53cSJulian Schmidt   }
1150eb7d53cSJulian Schmidt 
1160eb7d53cSJulian Schmidt   // Used for non-top-level matchers (i.e. matchers that are used as inner
1170eb7d53cSJulian Schmidt   // matchers for top-level matchers).
1180eb7d53cSJulian Schmidt   //
1190eb7d53cSJulian Schmidt   // E.g.: The matcher of `std::numbers::log2e` uses this matcher to check if
1200eb7d53cSJulian Schmidt   // `e` of `log2(e)` is declared constant and initialized with the value for
1210eb7d53cSJulian Schmidt   // eulers number.
1220eb7d53cSJulian Schmidt   //
1230eb7d53cSJulian Schmidt   // Here, we do care about literals and about DeclRefExprs to variable
1240eb7d53cSJulian Schmidt   // declarations that are constant and initialized with `Val`. This allows
1250eb7d53cSJulian Schmidt   // top-level matchers to see through declared constants for their inner
1260eb7d53cSJulian Schmidt   // matches like the `std::numbers::log2e` matcher.
matchFloatValueNear__anon60bcb47f0111::MatchBuilder1270eb7d53cSJulian Schmidt   auto matchFloatValueNear(const double Val) const {
1280eb7d53cSJulian Schmidt     const auto Float = floatLiteral(near(Val, DiffThreshold));
1290eb7d53cSJulian Schmidt 
1300eb7d53cSJulian Schmidt     const auto Dref = declRefExpr(
1310eb7d53cSJulian Schmidt         to(varDecl(hasType(qualType(isConstQualified(), isFloating())),
1320eb7d53cSJulian Schmidt                    hasInitializer(ignoreParenAndFloatingCasting(Float)))));
1330eb7d53cSJulian Schmidt     return expr(ignoreParenAndFloatingCasting(anyOf(Float, Dref)));
1340eb7d53cSJulian Schmidt   }
1350eb7d53cSJulian Schmidt 
matchValue__anon60bcb47f0111::MatchBuilder1360eb7d53cSJulian Schmidt   auto matchValue(const int64_t ValInt) const {
1370eb7d53cSJulian Schmidt     const auto Int =
1380eb7d53cSJulian Schmidt         expr(ignoreParenAndArithmeticCasting(integerLiteral(equals(ValInt))));
1390eb7d53cSJulian Schmidt     const auto Float = expr(ignoreParenAndFloatingCasting(
1400eb7d53cSJulian Schmidt         matchFloatValueNear(static_cast<double>(ValInt))));
1410eb7d53cSJulian Schmidt     const auto Dref = declRefExpr(to(varDecl(
1420eb7d53cSJulian Schmidt         hasType(qualType(isConstQualified(), isArithmetic())),
1430eb7d53cSJulian Schmidt         hasInitializer(expr(anyOf(ignoringImplicit(Int),
1440eb7d53cSJulian Schmidt                                   ignoreParenAndFloatingCasting(Float)))))));
1450eb7d53cSJulian Schmidt     return expr(anyOf(Int, Float, Dref));
1460eb7d53cSJulian Schmidt   }
1470eb7d53cSJulian Schmidt 
match1Div__anon60bcb47f0111::MatchBuilder1480eb7d53cSJulian Schmidt   auto match1Div(const Matcher<clang::Expr> Match) const {
1490eb7d53cSJulian Schmidt     return binaryOperator(hasOperatorName("/"), hasLHS(matchValue(1)),
1500eb7d53cSJulian Schmidt                           hasRHS(Match));
1510eb7d53cSJulian Schmidt   }
1520eb7d53cSJulian Schmidt 
matchEuler__anon60bcb47f0111::MatchBuilder1530eb7d53cSJulian Schmidt   auto matchEuler() const {
1540eb7d53cSJulian Schmidt     return expr(anyOf(matchFloatValueNear(llvm::numbers::e),
1550eb7d53cSJulian Schmidt                       matchMathCall("exp", matchValue(1))));
1560eb7d53cSJulian Schmidt   }
matchEulerTopLevel__anon60bcb47f0111::MatchBuilder1570eb7d53cSJulian Schmidt   auto matchEulerTopLevel() const {
1580eb7d53cSJulian Schmidt     return expr(anyOf(matchFloatLiteralNear("e_literal", llvm::numbers::e),
1590eb7d53cSJulian Schmidt                       matchMathCall("exp", matchValue(1)).bind("e_pattern")))
1600eb7d53cSJulian Schmidt         .bind("e");
1610eb7d53cSJulian Schmidt   }
1620eb7d53cSJulian Schmidt 
matchLog2Euler__anon60bcb47f0111::MatchBuilder1630eb7d53cSJulian Schmidt   auto matchLog2Euler() const {
1640eb7d53cSJulian Schmidt     return expr(
1650eb7d53cSJulian Schmidt                anyOf(
1660eb7d53cSJulian Schmidt                    matchFloatLiteralNear("log2e_literal", llvm::numbers::log2e),
1670eb7d53cSJulian Schmidt                    matchMathCall("log2", matchEuler()).bind("log2e_pattern")))
1680eb7d53cSJulian Schmidt         .bind("log2e");
1690eb7d53cSJulian Schmidt   }
1700eb7d53cSJulian Schmidt 
matchLog10Euler__anon60bcb47f0111::MatchBuilder1710eb7d53cSJulian Schmidt   auto matchLog10Euler() const {
1720eb7d53cSJulian Schmidt     return expr(
1730eb7d53cSJulian Schmidt                anyOf(
1740eb7d53cSJulian Schmidt                    matchFloatLiteralNear("log10e_literal",
1750eb7d53cSJulian Schmidt                                          llvm::numbers::log10e),
1760eb7d53cSJulian Schmidt                    matchMathCall("log10", matchEuler()).bind("log10e_pattern")))
1770eb7d53cSJulian Schmidt         .bind("log10e");
1780eb7d53cSJulian Schmidt   }
1790eb7d53cSJulian Schmidt 
matchPi__anon60bcb47f0111::MatchBuilder1800eb7d53cSJulian Schmidt   auto matchPi() const { return matchFloatValueNear(llvm::numbers::pi); }
matchPiTopLevel__anon60bcb47f0111::MatchBuilder1810eb7d53cSJulian Schmidt   auto matchPiTopLevel() const {
1820eb7d53cSJulian Schmidt     return matchFloatLiteralNear("pi_literal", llvm::numbers::pi).bind("pi");
1830eb7d53cSJulian Schmidt   }
1840eb7d53cSJulian Schmidt 
matchEgamma__anon60bcb47f0111::MatchBuilder1850eb7d53cSJulian Schmidt   auto matchEgamma() const {
1860eb7d53cSJulian Schmidt     return matchFloatLiteralNear("egamma_literal", llvm::numbers::egamma)
1870eb7d53cSJulian Schmidt         .bind("egamma");
1880eb7d53cSJulian Schmidt   }
1890eb7d53cSJulian Schmidt 
matchInvPi__anon60bcb47f0111::MatchBuilder1900eb7d53cSJulian Schmidt   auto matchInvPi() const {
1910eb7d53cSJulian Schmidt     return expr(anyOf(matchFloatLiteralNear("inv_pi_literal",
1920eb7d53cSJulian Schmidt                                             llvm::numbers::inv_pi),
1930eb7d53cSJulian Schmidt                       match1Div(matchPi()).bind("inv_pi_pattern")))
1940eb7d53cSJulian Schmidt         .bind("inv_pi");
1950eb7d53cSJulian Schmidt   }
1960eb7d53cSJulian Schmidt 
matchInvSqrtPi__anon60bcb47f0111::MatchBuilder1970eb7d53cSJulian Schmidt   auto matchInvSqrtPi() const {
1980eb7d53cSJulian Schmidt     return expr(anyOf(
1990eb7d53cSJulian Schmidt                     matchFloatLiteralNear("inv_sqrtpi_literal",
2000eb7d53cSJulian Schmidt                                           llvm::numbers::inv_sqrtpi),
2010eb7d53cSJulian Schmidt                     match1Div(matchSqrt(matchPi())).bind("inv_sqrtpi_pattern")))
2020eb7d53cSJulian Schmidt         .bind("inv_sqrtpi");
2030eb7d53cSJulian Schmidt   }
2040eb7d53cSJulian Schmidt 
matchLn2__anon60bcb47f0111::MatchBuilder2050eb7d53cSJulian Schmidt   auto matchLn2() const {
2060eb7d53cSJulian Schmidt     return expr(anyOf(matchFloatLiteralNear("ln2_literal", llvm::numbers::ln2),
2070eb7d53cSJulian Schmidt                       matchMathCall("log", matchValue(2)).bind("ln2_pattern")))
2080eb7d53cSJulian Schmidt         .bind("ln2");
2090eb7d53cSJulian Schmidt   }
2100eb7d53cSJulian Schmidt 
machterLn10__anon60bcb47f0111::MatchBuilder2110eb7d53cSJulian Schmidt   auto machterLn10() const {
2120eb7d53cSJulian Schmidt     return expr(
2130eb7d53cSJulian Schmidt                anyOf(matchFloatLiteralNear("ln10_literal", llvm::numbers::ln10),
2140eb7d53cSJulian Schmidt                      matchMathCall("log", matchValue(10)).bind("ln10_pattern")))
2150eb7d53cSJulian Schmidt         .bind("ln10");
2160eb7d53cSJulian Schmidt   }
2170eb7d53cSJulian Schmidt 
matchSqrt2__anon60bcb47f0111::MatchBuilder2180eb7d53cSJulian Schmidt   auto matchSqrt2() const {
2190eb7d53cSJulian Schmidt     return expr(anyOf(matchFloatLiteralNear("sqrt2_literal",
2200eb7d53cSJulian Schmidt                                             llvm::numbers::sqrt2),
2210eb7d53cSJulian Schmidt                       matchSqrt(matchValue(2)).bind("sqrt2_pattern")))
2220eb7d53cSJulian Schmidt         .bind("sqrt2");
2230eb7d53cSJulian Schmidt   }
2240eb7d53cSJulian Schmidt 
matchSqrt3__anon60bcb47f0111::MatchBuilder2250eb7d53cSJulian Schmidt   auto matchSqrt3() const {
2260eb7d53cSJulian Schmidt     return expr(anyOf(matchFloatLiteralNear("sqrt3_literal",
2270eb7d53cSJulian Schmidt                                             llvm::numbers::sqrt3),
2280eb7d53cSJulian Schmidt                       matchSqrt(matchValue(3)).bind("sqrt3_pattern")))
2290eb7d53cSJulian Schmidt         .bind("sqrt3");
2300eb7d53cSJulian Schmidt   }
2310eb7d53cSJulian Schmidt 
matchInvSqrt3__anon60bcb47f0111::MatchBuilder2320eb7d53cSJulian Schmidt   auto matchInvSqrt3() const {
2330eb7d53cSJulian Schmidt     return expr(anyOf(matchFloatLiteralNear("inv_sqrt3_literal",
2340eb7d53cSJulian Schmidt                                             llvm::numbers::inv_sqrt3),
2350eb7d53cSJulian Schmidt                       match1Div(matchSqrt(matchValue(3)))
2360eb7d53cSJulian Schmidt                           .bind("inv_sqrt3_pattern")))
2370eb7d53cSJulian Schmidt         .bind("inv_sqrt3");
2380eb7d53cSJulian Schmidt   }
2390eb7d53cSJulian Schmidt 
matchPhi__anon60bcb47f0111::MatchBuilder2400eb7d53cSJulian Schmidt   auto matchPhi() const {
2410eb7d53cSJulian Schmidt     const auto PhiFormula = binaryOperator(
2420eb7d53cSJulian Schmidt         hasOperatorName("/"),
2430eb7d53cSJulian Schmidt         hasLHS(binaryOperator(
2440eb7d53cSJulian Schmidt             hasOperatorName("+"), hasEitherOperand(matchValue(1)),
2450eb7d53cSJulian Schmidt             hasEitherOperand(matchMathCall("sqrt", matchValue(5))))),
2460eb7d53cSJulian Schmidt         hasRHS(matchValue(2)));
2470eb7d53cSJulian Schmidt     return expr(anyOf(PhiFormula.bind("phi_pattern"),
2480eb7d53cSJulian Schmidt                       matchFloatLiteralNear("phi_literal", llvm::numbers::phi)))
2490eb7d53cSJulian Schmidt         .bind("phi");
2500eb7d53cSJulian Schmidt   }
2510eb7d53cSJulian Schmidt 
2520eb7d53cSJulian Schmidt   double DiffThreshold;
2530eb7d53cSJulian Schmidt };
2540eb7d53cSJulian Schmidt 
getCode(const StringRef Constant,const bool IsFloat,const bool IsLongDouble)2550eb7d53cSJulian Schmidt std::string getCode(const StringRef Constant, const bool IsFloat,
2560eb7d53cSJulian Schmidt                     const bool IsLongDouble) {
2570eb7d53cSJulian Schmidt   if (IsFloat) {
2580eb7d53cSJulian Schmidt     return ("std::numbers::" + Constant + "_v<float>").str();
2590eb7d53cSJulian Schmidt   }
2600eb7d53cSJulian Schmidt   if (IsLongDouble) {
2610eb7d53cSJulian Schmidt     return ("std::numbers::" + Constant + "_v<long double>").str();
2620eb7d53cSJulian Schmidt   }
2630eb7d53cSJulian Schmidt   return ("std::numbers::" + Constant).str();
2640eb7d53cSJulian Schmidt }
2650eb7d53cSJulian Schmidt 
isRangeOfCompleteMacro(const clang::SourceRange & Range,const clang::SourceManager & SM,const clang::LangOptions & LO)2660eb7d53cSJulian Schmidt bool isRangeOfCompleteMacro(const clang::SourceRange &Range,
2670eb7d53cSJulian Schmidt                             const clang::SourceManager &SM,
2680eb7d53cSJulian Schmidt                             const clang::LangOptions &LO) {
2690eb7d53cSJulian Schmidt   if (!Range.getBegin().isMacroID()) {
2700eb7d53cSJulian Schmidt     return false;
2710eb7d53cSJulian Schmidt   }
2720eb7d53cSJulian Schmidt   if (!clang::Lexer::isAtStartOfMacroExpansion(Range.getBegin(), SM, LO)) {
2730eb7d53cSJulian Schmidt     return false;
2740eb7d53cSJulian Schmidt   }
2750eb7d53cSJulian Schmidt 
2760eb7d53cSJulian Schmidt   if (!Range.getEnd().isMacroID()) {
2770eb7d53cSJulian Schmidt     return false;
2780eb7d53cSJulian Schmidt   }
2790eb7d53cSJulian Schmidt 
2800eb7d53cSJulian Schmidt   if (!clang::Lexer::isAtEndOfMacroExpansion(Range.getEnd(), SM, LO)) {
2810eb7d53cSJulian Schmidt     return false;
2820eb7d53cSJulian Schmidt   }
2830eb7d53cSJulian Schmidt 
2840eb7d53cSJulian Schmidt   return true;
2850eb7d53cSJulian Schmidt }
2860eb7d53cSJulian Schmidt 
2870eb7d53cSJulian Schmidt } // namespace
2880eb7d53cSJulian Schmidt 
2890eb7d53cSJulian Schmidt namespace clang::tidy::modernize {
UseStdNumbersCheck(const StringRef Name,ClangTidyContext * const Context)2900eb7d53cSJulian Schmidt UseStdNumbersCheck::UseStdNumbersCheck(const StringRef Name,
2910eb7d53cSJulian Schmidt                                        ClangTidyContext *const Context)
2920eb7d53cSJulian Schmidt     : ClangTidyCheck(Name, Context),
2930eb7d53cSJulian Schmidt       IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
2940eb7d53cSJulian Schmidt                                                utils::IncludeSorter::IS_LLVM),
2950eb7d53cSJulian Schmidt                       areDiagsSelfContained()),
2960eb7d53cSJulian Schmidt       DiffThresholdString{Options.get("DiffThreshold", "0.001")} {
2970eb7d53cSJulian Schmidt   if (DiffThresholdString.getAsDouble(DiffThreshold)) {
2980eb7d53cSJulian Schmidt     configurationDiag(
2990eb7d53cSJulian Schmidt         "Invalid DiffThreshold config value: '%0', expected a double")
3000eb7d53cSJulian Schmidt         << DiffThresholdString;
3010eb7d53cSJulian Schmidt     DiffThreshold = 0.001;
3020eb7d53cSJulian Schmidt   }
3030eb7d53cSJulian Schmidt }
3040eb7d53cSJulian Schmidt 
registerMatchers(MatchFinder * const Finder)3050eb7d53cSJulian Schmidt void UseStdNumbersCheck::registerMatchers(MatchFinder *const Finder) {
3060eb7d53cSJulian Schmidt   const auto Matches = MatchBuilder{DiffThreshold};
307e1fa2feaSPiotr Zegar   std::vector<Matcher<clang::Stmt>> ConstantMatchers = {
3080eb7d53cSJulian Schmidt       Matches.matchLog2Euler(),     Matches.matchLog10Euler(),
3090eb7d53cSJulian Schmidt       Matches.matchEulerTopLevel(), Matches.matchEgamma(),
3100eb7d53cSJulian Schmidt       Matches.matchInvSqrtPi(),     Matches.matchInvPi(),
3110eb7d53cSJulian Schmidt       Matches.matchPiTopLevel(),    Matches.matchLn2(),
3120eb7d53cSJulian Schmidt       Matches.machterLn10(),        Matches.matchSqrt2(),
3130eb7d53cSJulian Schmidt       Matches.matchInvSqrt3(),      Matches.matchSqrt3(),
3140eb7d53cSJulian Schmidt       Matches.matchPhi(),
3150eb7d53cSJulian Schmidt   };
3160eb7d53cSJulian Schmidt 
3170eb7d53cSJulian Schmidt   Finder->addMatcher(
3180eb7d53cSJulian Schmidt       expr(
319e1fa2feaSPiotr Zegar           anyOfExhaustive(std::move(ConstantMatchers)),
3200eb7d53cSJulian Schmidt           unless(hasParent(explicitCastExpr(hasDestinationType(isFloating())))),
3210eb7d53cSJulian Schmidt           hasType(qualType(hasCanonicalTypeUnqualified(
3220eb7d53cSJulian Schmidt               anyOf(qualType(asString("float")).bind("float"),
3230eb7d53cSJulian Schmidt                     qualType(asString("double")),
3240eb7d53cSJulian Schmidt                     qualType(asString("long double")).bind("long double")))))),
3250eb7d53cSJulian Schmidt       this);
3260eb7d53cSJulian Schmidt }
3270eb7d53cSJulian Schmidt 
check(const MatchFinder::MatchResult & Result)3280eb7d53cSJulian Schmidt void UseStdNumbersCheck::check(const MatchFinder::MatchResult &Result) {
3290eb7d53cSJulian Schmidt   /*
3300eb7d53cSJulian Schmidt     List of all math constants in the `<numbers>` header
3310eb7d53cSJulian Schmidt     + e
3320eb7d53cSJulian Schmidt     + log2e
3330eb7d53cSJulian Schmidt     + log10e
3340eb7d53cSJulian Schmidt     + pi
3350eb7d53cSJulian Schmidt     + inv_pi
3360eb7d53cSJulian Schmidt     + inv_sqrtpi
3370eb7d53cSJulian Schmidt     + ln2
3380eb7d53cSJulian Schmidt     + ln10
3390eb7d53cSJulian Schmidt     + sqrt2
3400eb7d53cSJulian Schmidt     + sqrt3
3410eb7d53cSJulian Schmidt     + inv_sqrt3
3420eb7d53cSJulian Schmidt     + egamma
3430eb7d53cSJulian Schmidt     + phi
3440eb7d53cSJulian Schmidt   */
3450eb7d53cSJulian Schmidt 
3460eb7d53cSJulian Schmidt   // The ordering determines what constants are looked at first.
3470eb7d53cSJulian Schmidt   // E.g. look at 'inv_sqrt3' before 'sqrt3' to be able to replace the larger
3480eb7d53cSJulian Schmidt   // expression
3490eb7d53cSJulian Schmidt   constexpr auto Constants = std::array<std::pair<StringRef, double>, 13>{
3500eb7d53cSJulian Schmidt       std::pair{StringRef{"log2e"}, llvm::numbers::log2e},
3510eb7d53cSJulian Schmidt       std::pair{StringRef{"log10e"}, llvm::numbers::log10e},
3520eb7d53cSJulian Schmidt       std::pair{StringRef{"e"}, llvm::numbers::e},
3530eb7d53cSJulian Schmidt       std::pair{StringRef{"egamma"}, llvm::numbers::egamma},
3540eb7d53cSJulian Schmidt       std::pair{StringRef{"inv_sqrtpi"}, llvm::numbers::inv_sqrtpi},
3550eb7d53cSJulian Schmidt       std::pair{StringRef{"inv_pi"}, llvm::numbers::inv_pi},
3560eb7d53cSJulian Schmidt       std::pair{StringRef{"pi"}, llvm::numbers::pi},
3570eb7d53cSJulian Schmidt       std::pair{StringRef{"ln2"}, llvm::numbers::ln2},
3580eb7d53cSJulian Schmidt       std::pair{StringRef{"ln10"}, llvm::numbers::ln10},
3590eb7d53cSJulian Schmidt       std::pair{StringRef{"sqrt2"}, llvm::numbers::sqrt2},
3600eb7d53cSJulian Schmidt       std::pair{StringRef{"inv_sqrt3"}, llvm::numbers::inv_sqrt3},
3610eb7d53cSJulian Schmidt       std::pair{StringRef{"sqrt3"}, llvm::numbers::sqrt3},
3620eb7d53cSJulian Schmidt       std::pair{StringRef{"phi"}, llvm::numbers::phi},
3630eb7d53cSJulian Schmidt   };
3640eb7d53cSJulian Schmidt 
3650eb7d53cSJulian Schmidt   auto MatchedLiterals =
3660eb7d53cSJulian Schmidt       llvm::SmallVector<std::tuple<std::string, double, const Expr *>>{};
3670eb7d53cSJulian Schmidt 
3680eb7d53cSJulian Schmidt   const auto &SM = *Result.SourceManager;
3690eb7d53cSJulian Schmidt   const auto &LO = Result.Context->getLangOpts();
3700eb7d53cSJulian Schmidt 
3710eb7d53cSJulian Schmidt   const auto IsFloat = Result.Nodes.getNodeAs<QualType>("float") != nullptr;
3720eb7d53cSJulian Schmidt   const auto IsLongDouble =
3730eb7d53cSJulian Schmidt       Result.Nodes.getNodeAs<QualType>("long double") != nullptr;
3740eb7d53cSJulian Schmidt 
3750eb7d53cSJulian Schmidt   for (const auto &[ConstantName, ConstantValue] : Constants) {
3760eb7d53cSJulian Schmidt     const auto *const Match = Result.Nodes.getNodeAs<Expr>(ConstantName);
3770eb7d53cSJulian Schmidt     if (Match == nullptr) {
3780eb7d53cSJulian Schmidt       continue;
3790eb7d53cSJulian Schmidt     }
3800eb7d53cSJulian Schmidt 
3810eb7d53cSJulian Schmidt     const auto Range = Match->getSourceRange();
3820eb7d53cSJulian Schmidt 
3830eb7d53cSJulian Schmidt     const auto IsMacro = Range.getBegin().isMacroID();
3840eb7d53cSJulian Schmidt 
3850eb7d53cSJulian Schmidt     // We do not want to emit a diagnostic when we are matching a macro, but the
3860eb7d53cSJulian Schmidt     // match inside of the macro does not cover the whole macro.
3870eb7d53cSJulian Schmidt     if (IsMacro && !isRangeOfCompleteMacro(Range, SM, LO)) {
3880eb7d53cSJulian Schmidt       continue;
3890eb7d53cSJulian Schmidt     }
3900eb7d53cSJulian Schmidt 
3910eb7d53cSJulian Schmidt     if (const auto PatternBindString = (ConstantName + "_pattern").str();
3920eb7d53cSJulian Schmidt         Result.Nodes.getNodeAs<Expr>(PatternBindString) != nullptr) {
3930eb7d53cSJulian Schmidt       const auto Code = getCode(ConstantName, IsFloat, IsLongDouble);
3940eb7d53cSJulian Schmidt       diag(Range.getBegin(), "prefer '%0' to this %select{formula|macro}1")
3950eb7d53cSJulian Schmidt           << Code << IsMacro << FixItHint::CreateReplacement(Range, Code);
3960eb7d53cSJulian Schmidt       return;
3970eb7d53cSJulian Schmidt     }
3980eb7d53cSJulian Schmidt 
3990eb7d53cSJulian Schmidt     const auto LiteralBindString = (ConstantName + "_literal").str();
4000eb7d53cSJulian Schmidt     if (const auto *const Literal =
4010eb7d53cSJulian Schmidt             Result.Nodes.getNodeAs<FloatingLiteral>(LiteralBindString)) {
4020eb7d53cSJulian Schmidt       MatchedLiterals.emplace_back(
4030eb7d53cSJulian Schmidt           ConstantName,
4040eb7d53cSJulian Schmidt           std::abs(Literal->getValueAsApproximateDouble() - ConstantValue),
4050eb7d53cSJulian Schmidt           Match);
4060eb7d53cSJulian Schmidt     }
4070eb7d53cSJulian Schmidt   }
4080eb7d53cSJulian Schmidt 
4090eb7d53cSJulian Schmidt   // We may have had no matches with literals, but a match with a pattern that
4100eb7d53cSJulian Schmidt   // was a part of a macro which was therefore skipped.
4110eb7d53cSJulian Schmidt   if (MatchedLiterals.empty()) {
4120eb7d53cSJulian Schmidt     return;
4130eb7d53cSJulian Schmidt   }
4140eb7d53cSJulian Schmidt 
4150eb7d53cSJulian Schmidt   llvm::sort(MatchedLiterals, [](const auto &LHS, const auto &RHS) {
4160eb7d53cSJulian Schmidt     return std::get<1>(LHS) < std::get<1>(RHS);
4170eb7d53cSJulian Schmidt   });
4180eb7d53cSJulian Schmidt 
4190eb7d53cSJulian Schmidt   const auto &[Constant, Diff, Node] = MatchedLiterals.front();
4200eb7d53cSJulian Schmidt 
4210eb7d53cSJulian Schmidt   const auto Range = Node->getSourceRange();
4220eb7d53cSJulian Schmidt   const auto IsMacro = Range.getBegin().isMacroID();
4230eb7d53cSJulian Schmidt 
4240eb7d53cSJulian Schmidt   // We do not want to emit a diagnostic when we are matching a macro, but the
4250eb7d53cSJulian Schmidt   // match inside of the macro does not cover the whole macro.
4260eb7d53cSJulian Schmidt   if (IsMacro && !isRangeOfCompleteMacro(Range, SM, LO)) {
4270eb7d53cSJulian Schmidt     return;
4280eb7d53cSJulian Schmidt   }
4290eb7d53cSJulian Schmidt 
4300eb7d53cSJulian Schmidt   const auto Code = getCode(Constant, IsFloat, IsLongDouble);
4310eb7d53cSJulian Schmidt   diag(Range.getBegin(),
4320eb7d53cSJulian Schmidt        "prefer '%0' to this %select{literal|macro}1, differs by '%2'")
4330eb7d53cSJulian Schmidt       << Code << IsMacro << llvm::formatv("{0:e2}", Diff).str()
4340eb7d53cSJulian Schmidt       << FixItHint::CreateReplacement(Range, Code)
4350eb7d53cSJulian Schmidt       << IncludeInserter.createIncludeInsertion(
4360eb7d53cSJulian Schmidt              Result.SourceManager->getFileID(Range.getBegin()), "<numbers>");
4370eb7d53cSJulian Schmidt }
4380eb7d53cSJulian Schmidt 
registerPPCallbacks(const SourceManager & SM,Preprocessor * const PP,Preprocessor * const ModuleExpanderPP)4390eb7d53cSJulian Schmidt void UseStdNumbersCheck::registerPPCallbacks(
4400eb7d53cSJulian Schmidt     const SourceManager &SM, Preprocessor *const PP,
4410eb7d53cSJulian Schmidt     Preprocessor *const ModuleExpanderPP) {
4420eb7d53cSJulian Schmidt   IncludeInserter.registerPreprocessor(PP);
4430eb7d53cSJulian Schmidt }
4440eb7d53cSJulian Schmidt 
storeOptions(ClangTidyOptions::OptionMap & Opts)4450eb7d53cSJulian Schmidt void UseStdNumbersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
4460eb7d53cSJulian Schmidt   Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
4470eb7d53cSJulian Schmidt   Options.store(Opts, "DiffThreshold", DiffThresholdString);
4480eb7d53cSJulian Schmidt }
4490eb7d53cSJulian Schmidt } // namespace clang::tidy::modernize
450