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