10#include "../ClangTidyDiagnosticConsumer.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/AST/Decl.h"
13#include "clang/AST/Expr.h"
14#include "clang/AST/Stmt.h"
15#include "clang/AST/Type.h"
16#include "clang/ASTMatchers/ASTMatchFinder.h"
17#include "clang/ASTMatchers/ASTMatchers.h"
18#include "clang/ASTMatchers/ASTMatchersInternal.h"
19#include "clang/ASTMatchers/ASTMatchersMacros.h"
20#include "clang/Basic/Diagnostic.h"
21#include "clang/Basic/LLVM.h"
22#include "clang/Basic/LangOptions.h"
23#include "clang/Basic/SourceLocation.h"
24#include "clang/Basic/SourceManager.h"
25#include "clang/Lex/Lexer.h"
26#include "llvm/ADT/STLExtras.h"
27#include "llvm/ADT/SmallVector.h"
28#include "llvm/ADT/StringRef.h"
29#include "llvm/Support/FormatVariadic.h"
30#include "llvm/Support/MathExtras.h"
35#include <initializer_list>
42using clang::ast_matchers::internal::Matcher;
45AST_MATCHER_P2(clang::FloatingLiteral, near,
double, Value,
double,
47 return std::abs(
Node.getValueAsApproximateDouble() - Value) < DiffThreshold;
51 Matcher<clang::QualType>, InnerMatcher) {
52 return !
Node.isNull() &&
53 InnerMatcher.matches(
Node->getCanonicalTypeUnqualified(), Finder,
58 return !
Node.isNull() &&
Node->isArithmeticType();
61 return !
Node.isNull() &&
Node->isFloatingType();
64AST_MATCHER_P(clang::Expr, anyOfExhaustive, std::vector<Matcher<clang::Stmt>>,
66 bool FoundMatch =
false;
67 for (
const auto &InnerMatcher : Exprs) {
68 clang::ast_matchers::internal::BoundNodesTreeBuilder Result = *
Builder;
69 if (InnerMatcher.matches(Node, Finder, &Result)) {
84 ignoreParenAndArithmeticCasting(
const Matcher<clang::Expr> Matcher)
const {
85 return expr(hasType(qualType(isArithmetic())), ignoringParenCasts(Matcher));
88 auto ignoreParenAndFloatingCasting(
const Matcher<clang::Expr> Matcher)
const {
89 return expr(hasType(qualType(isFloating())), ignoringParenCasts(Matcher));
92 auto matchMathCall(
const StringRef FunctionName,
93 const Matcher<clang::Expr> ArgumentMatcher)
const {
94 return expr(ignoreParenAndFloatingCasting(
95 callExpr(callee(functionDecl(hasName(FunctionName),
96 hasParameter(0, hasType(isArithmetic())))),
97 hasArgument(0, ArgumentMatcher))));
100 auto matchSqrt(
const Matcher<clang::Expr> ArgumentMatcher)
const {
101 return matchMathCall(
"sqrt", ArgumentMatcher);
111 auto matchFloatLiteralNear(
const StringRef Constant,
const double Val)
const {
112 return expr(ignoreParenAndFloatingCasting(
113 floatLiteral(near(Val, DiffThreshold)).bind(Constant)));
127 auto matchFloatValueNear(
const double Val)
const {
128 const auto Float = floatLiteral(near(Val, DiffThreshold));
130 const auto Dref = declRefExpr(
131 to(varDecl(hasType(qualType(isConstQualified(), isFloating())),
132 hasInitializer(ignoreParenAndFloatingCasting(Float)))));
133 return expr(ignoreParenAndFloatingCasting(anyOf(Float, Dref)));
136 auto matchValue(
const int64_t ValInt)
const {
138 expr(ignoreParenAndArithmeticCasting(integerLiteral(equals(ValInt))));
139 const auto Float = expr(ignoreParenAndFloatingCasting(
140 matchFloatValueNear(
static_cast<double>(ValInt))));
141 const auto Dref = declRefExpr(to(varDecl(
142 hasType(qualType(isConstQualified(), isArithmetic())),
143 hasInitializer(expr(anyOf(ignoringImplicit(Int),
144 ignoreParenAndFloatingCasting(Float)))))));
145 return expr(anyOf(Int, Float, Dref));
148 auto match1Div(
const Matcher<clang::Expr> Match)
const {
149 return binaryOperator(hasOperatorName(
"/"), hasLHS(matchValue(1)),
153 auto matchEuler()
const {
154 return expr(anyOf(matchFloatValueNear(llvm::numbers::e),
155 matchMathCall(
"exp", matchValue(1))));
157 auto matchEulerTopLevel()
const {
158 return expr(anyOf(matchFloatLiteralNear(
"e_literal", llvm::numbers::e),
159 matchMathCall(
"exp", matchValue(1)).bind(
"e_pattern")))
163 auto matchLog2Euler()
const {
166 matchFloatLiteralNear(
"log2e_literal", llvm::numbers::log2e),
167 matchMathCall(
"log2", matchEuler()).bind(
"log2e_pattern")))
171 auto matchLog10Euler()
const {
174 matchFloatLiteralNear(
"log10e_literal",
175 llvm::numbers::log10e),
176 matchMathCall(
"log10", matchEuler()).bind(
"log10e_pattern")))
180 auto matchPi()
const {
return matchFloatValueNear(llvm::numbers::pi); }
181 auto matchPiTopLevel()
const {
182 return matchFloatLiteralNear(
"pi_literal", llvm::numbers::pi).bind(
"pi");
185 auto matchEgamma()
const {
186 return matchFloatLiteralNear(
"egamma_literal", llvm::numbers::egamma)
190 auto matchInvPi()
const {
191 return expr(anyOf(matchFloatLiteralNear(
"inv_pi_literal",
192 llvm::numbers::inv_pi),
193 match1Div(matchPi()).bind(
"inv_pi_pattern")))
197 auto matchInvSqrtPi()
const {
199 matchFloatLiteralNear(
"inv_sqrtpi_literal",
200 llvm::numbers::inv_sqrtpi),
201 match1Div(matchSqrt(matchPi())).bind(
"inv_sqrtpi_pattern")))
205 auto matchLn2()
const {
206 return expr(anyOf(matchFloatLiteralNear(
"ln2_literal", llvm::numbers::ln2),
207 matchMathCall(
"log", matchValue(2)).bind(
"ln2_pattern")))
211 auto machterLn10()
const {
213 anyOf(matchFloatLiteralNear(
"ln10_literal", llvm::numbers::ln10),
214 matchMathCall(
"log", matchValue(10)).bind(
"ln10_pattern")))
218 auto matchSqrt2()
const {
219 return expr(anyOf(matchFloatLiteralNear(
"sqrt2_literal",
220 llvm::numbers::sqrt2),
221 matchSqrt(matchValue(2)).bind(
"sqrt2_pattern")))
225 auto matchSqrt3()
const {
226 return expr(anyOf(matchFloatLiteralNear(
"sqrt3_literal",
227 llvm::numbers::sqrt3),
228 matchSqrt(matchValue(3)).bind(
"sqrt3_pattern")))
232 auto matchInvSqrt3()
const {
233 return expr(anyOf(matchFloatLiteralNear(
"inv_sqrt3_literal",
234 llvm::numbers::inv_sqrt3),
235 match1Div(matchSqrt(matchValue(3)))
236 .bind(
"inv_sqrt3_pattern")))
240 auto matchPhi()
const {
241 const auto PhiFormula = binaryOperator(
242 hasOperatorName(
"/"),
243 hasLHS(binaryOperator(
244 hasOperatorName(
"+"), hasEitherOperand(matchValue(1)),
245 hasEitherOperand(matchMathCall(
"sqrt", matchValue(5))))),
246 hasRHS(matchValue(2)));
247 return expr(anyOf(PhiFormula.bind(
"phi_pattern"),
248 matchFloatLiteralNear(
"phi_literal", llvm::numbers::phi)))
252 double DiffThreshold;
255std::string getCode(
const StringRef Constant,
const bool IsFloat,
256 const bool IsLongDouble) {
258 return (
"std::numbers::" + Constant +
"_v<float>").str();
261 return (
"std::numbers::" + Constant +
"_v<long double>").str();
263 return (
"std::numbers::" + Constant).str();
266bool isRangeOfCompleteMacro(
const clang::SourceRange &Range,
267 const clang::SourceManager &SM,
268 const clang::LangOptions &LO) {
269 if (!
Range.getBegin().isMacroID()) {
272 if (!clang::Lexer::isAtStartOfMacroExpansion(
Range.getBegin(), SM, LO)) {
276 if (!
Range.getEnd().isMacroID()) {
280 if (!clang::Lexer::isAtEndOfMacroExpansion(
Range.getEnd(), SM, LO)) {
293 IncludeInserter(Options.getLocalOrGlobal(
"IncludeStyle",
294 utils::IncludeSorter::IS_LLVM),
295 areDiagsSelfContained()),
296 DiffThresholdString{Options.get(
"DiffThreshold",
"0.001")} {
297 if (DiffThresholdString.getAsDouble(DiffThreshold)) {
299 "Invalid DiffThreshold config value: '%0', expected a double")
300 << DiffThresholdString;
301 DiffThreshold = 0.001;
306 const auto Matches = MatchBuilder{DiffThreshold};
307 std::vector<Matcher<clang::Stmt>> ConstantMatchers = {
308 Matches.matchLog2Euler(), Matches.matchLog10Euler(),
309 Matches.matchEulerTopLevel(), Matches.matchEgamma(),
310 Matches.matchInvSqrtPi(), Matches.matchInvPi(),
311 Matches.matchPiTopLevel(), Matches.matchLn2(),
312 Matches.machterLn10(), Matches.matchSqrt2(),
313 Matches.matchInvSqrt3(), Matches.matchSqrt3(),
319 anyOfExhaustive(std::move(ConstantMatchers)),
320 unless(hasParent(explicitCastExpr(hasDestinationType(isFloating())))),
321 hasType(qualType(hasCanonicalTypeUnqualified(
322 anyOf(qualType(asString(
"float")).bind(
"float"),
323 qualType(asString(
"double")),
324 qualType(asString(
"long double")).bind(
"long double")))))),
349 constexpr auto Constants = std::array<std::pair<StringRef, double>, 13>{
350 std::pair{StringRef{
"log2e"}, llvm::numbers::log2e},
351 std::pair{StringRef{
"log10e"}, llvm::numbers::log10e},
352 std::pair{StringRef{
"e"}, llvm::numbers::e},
353 std::pair{StringRef{
"egamma"}, llvm::numbers::egamma},
354 std::pair{StringRef{
"inv_sqrtpi"}, llvm::numbers::inv_sqrtpi},
355 std::pair{StringRef{
"inv_pi"}, llvm::numbers::inv_pi},
356 std::pair{StringRef{
"pi"}, llvm::numbers::pi},
357 std::pair{StringRef{
"ln2"}, llvm::numbers::ln2},
358 std::pair{StringRef{
"ln10"}, llvm::numbers::ln10},
359 std::pair{StringRef{
"sqrt2"}, llvm::numbers::sqrt2},
360 std::pair{StringRef{
"inv_sqrt3"}, llvm::numbers::inv_sqrt3},
361 std::pair{StringRef{
"sqrt3"}, llvm::numbers::sqrt3},
362 std::pair{StringRef{
"phi"}, llvm::numbers::phi},
365 auto MatchedLiterals =
366 llvm::SmallVector<std::tuple<std::string, double, const Expr *>>{};
368 const auto &SM = *Result.SourceManager;
369 const auto &LO = Result.Context->getLangOpts();
371 const auto IsFloat = Result.Nodes.getNodeAs<QualType>(
"float") !=
nullptr;
372 const auto IsLongDouble =
373 Result.Nodes.getNodeAs<QualType>(
"long double") !=
nullptr;
375 for (
const auto &[ConstantName, ConstantValue] : Constants) {
376 const auto *
const Match = Result.Nodes.getNodeAs<Expr>(ConstantName);
377 if (Match ==
nullptr) {
381 const auto Range = Match->getSourceRange();
383 const auto IsMacro =
Range.getBegin().isMacroID();
387 if (IsMacro && !isRangeOfCompleteMacro(
Range, SM, LO)) {
391 if (
const auto PatternBindString = (ConstantName +
"_pattern").str();
392 Result.Nodes.getNodeAs<Expr>(PatternBindString) !=
nullptr) {
393 const auto Code = getCode(ConstantName, IsFloat, IsLongDouble);
394 diag(
Range.getBegin(),
"prefer '%0' to this %select{formula|macro}1")
395 <<
Code << IsMacro << FixItHint::CreateReplacement(
Range,
Code);
399 const auto LiteralBindString = (ConstantName +
"_literal").str();
400 if (
const auto *
const Literal =
401 Result.Nodes.getNodeAs<FloatingLiteral>(LiteralBindString)) {
402 MatchedLiterals.emplace_back(
404 std::abs(Literal->getValueAsApproximateDouble() - ConstantValue),
411 if (MatchedLiterals.empty()) {
415 llvm::sort(MatchedLiterals, [](
const auto &LHS,
const auto &RHS) {
416 return std::get<1>(LHS) < std::get<1>(RHS);
419 const auto &[Constant, Diff,
Node] = MatchedLiterals.front();
421 const auto Range =
Node->getSourceRange();
422 const auto IsMacro =
Range.getBegin().isMacroID();
426 if (IsMacro && !isRangeOfCompleteMacro(
Range, SM, LO)) {
430 const auto Code = getCode(Constant, IsFloat, IsLongDouble);
432 "prefer '%0' to this %select{literal|macro}1, differs by '%2'")
433 <<
Code << IsMacro << llvm::formatv(
"{0:e2}", Diff).str()
434 << FixItHint::CreateReplacement(
Range,
Code)
436 Result.SourceManager->getFileID(
Range.getBegin()),
"<numbers>");
440 const SourceManager &SM, Preprocessor *
const PP,
441 Preprocessor *
const ModuleExpanderPP) {
447 Options.
store(Opts,
"DiffThreshold", DiffThresholdString);
llvm::SmallString< 256U > Name
CodeCompletionBuilder Builder
CharSourceRange Range
SourceRange for the file name.
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
DiagnosticBuilder configurationDiag(StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning) const
Adds a diagnostic to report errors in the check's configuration.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
UseStdNumbersCheck(StringRef Name, ClangTidyContext *Context)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void registerPreprocessor(Preprocessor *PP)
Registers this with the Preprocessor PP, must be called before this class is used.
std::optional< FixItHint > createIncludeInsertion(FileID FileID, llvm::StringRef Header)
Creates a Header inclusion directive fixit in the File FileID.
IncludeSorter::IncludeStyle getStyle() const
AST_MATCHER_P(UserDefinedLiteral, hasLiteral, clang::ast_matchers::internal::Matcher< Expr >, InnerMatcher)
AST_MATCHER(Decl, declHasNoReturnAttr)
matches a Decl if it has a "no return" attribute of any kind
llvm::StringMap< ClangTidyValue > OptionMap