1 //===--- TypePromotionInMathFnCheck.cpp - clang-tidy-----------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "TypePromotionInMathFnCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Frontend/CompilerInstance.h"
13 #include "clang/Lex/Preprocessor.h"
14 #include "llvm/ADT/StringSet.h"
16 using namespace clang::ast_matchers
;
18 namespace clang::tidy::performance
{
21 AST_MATCHER_P(Type
, isBuiltinType
, BuiltinType::Kind
, Kind
) {
22 if (const auto *BT
= dyn_cast
<BuiltinType
>(&Node
)) {
23 return BT
->getKind() == Kind
;
27 } // anonymous namespace
29 TypePromotionInMathFnCheck::TypePromotionInMathFnCheck(
30 StringRef Name
, ClangTidyContext
*Context
)
31 : ClangTidyCheck(Name
, Context
),
32 IncludeInserter(Options
.getLocalOrGlobal("IncludeStyle",
33 utils::IncludeSorter::IS_LLVM
),
34 areDiagsSelfContained()) {}
36 void TypePromotionInMathFnCheck::registerPPCallbacks(
37 const SourceManager
&SM
, Preprocessor
*PP
, Preprocessor
*ModuleExpanderPP
) {
38 IncludeInserter
.registerPreprocessor(PP
);
41 void TypePromotionInMathFnCheck::storeOptions(
42 ClangTidyOptions::OptionMap
&Opts
) {
43 Options
.store(Opts
, "IncludeStyle", IncludeInserter
.getStyle());
46 void TypePromotionInMathFnCheck::registerMatchers(MatchFinder
*Finder
) {
47 constexpr BuiltinType::Kind IntTy
= BuiltinType::Int
;
48 constexpr BuiltinType::Kind LongTy
= BuiltinType::Long
;
49 constexpr BuiltinType::Kind FloatTy
= BuiltinType::Float
;
50 constexpr BuiltinType::Kind DoubleTy
= BuiltinType::Double
;
51 constexpr BuiltinType::Kind LongDoubleTy
= BuiltinType::LongDouble
;
53 auto HasBuiltinTyParam
= [](int Pos
, BuiltinType::Kind Kind
) {
54 return hasParameter(Pos
, hasType(isBuiltinType(Kind
)));
56 auto HasBuiltinTyArg
= [](int Pos
, BuiltinType::Kind Kind
) {
57 return hasArgument(Pos
, hasType(isBuiltinType(Kind
)));
60 // Match calls to foo(double) with a float argument.
61 auto OneDoubleArgFns
= hasAnyName(
62 "::acos", "::acosh", "::asin", "::asinh", "::atan", "::atanh", "::cbrt",
63 "::ceil", "::cos", "::cosh", "::erf", "::erfc", "::exp", "::exp2",
64 "::expm1", "::fabs", "::floor", "::ilogb", "::lgamma", "::llrint",
65 "::log", "::log10", "::log1p", "::log2", "::logb", "::lrint", "::modf",
66 "::nearbyint", "::rint", "::round", "::sin", "::sinh", "::sqrt", "::tan",
67 "::tanh", "::tgamma", "::trunc", "::llround", "::lround");
69 callExpr(callee(functionDecl(OneDoubleArgFns
, parameterCountIs(1),
70 HasBuiltinTyParam(0, DoubleTy
))),
71 HasBuiltinTyArg(0, FloatTy
))
75 // Match calls to foo(double, double) where both args are floats.
76 auto TwoDoubleArgFns
= hasAnyName("::atan2", "::copysign", "::fdim", "::fmax",
77 "::fmin", "::fmod", "::hypot", "::ldexp",
78 "::nextafter", "::pow", "::remainder");
80 callExpr(callee(functionDecl(TwoDoubleArgFns
, parameterCountIs(2),
81 HasBuiltinTyParam(0, DoubleTy
),
82 HasBuiltinTyParam(1, DoubleTy
))),
83 HasBuiltinTyArg(0, FloatTy
), HasBuiltinTyArg(1, FloatTy
))
87 // Match calls to fma(double, double, double) where all args are floats.
89 callExpr(callee(functionDecl(hasName("::fma"), parameterCountIs(3),
90 HasBuiltinTyParam(0, DoubleTy
),
91 HasBuiltinTyParam(1, DoubleTy
),
92 HasBuiltinTyParam(2, DoubleTy
))),
93 HasBuiltinTyArg(0, FloatTy
), HasBuiltinTyArg(1, FloatTy
),
94 HasBuiltinTyArg(2, FloatTy
))
98 // Match calls to frexp(double, int*) where the first arg is a float.
100 callExpr(callee(functionDecl(
101 hasName("::frexp"), parameterCountIs(2),
102 HasBuiltinTyParam(0, DoubleTy
),
103 hasParameter(1, parmVarDecl(hasType(pointerType(
104 pointee(isBuiltinType(IntTy
)))))))),
105 HasBuiltinTyArg(0, FloatTy
))
109 // Match calls to nexttoward(double, long double) where the first arg is a
112 callExpr(callee(functionDecl(hasName("::nexttoward"), parameterCountIs(2),
113 HasBuiltinTyParam(0, DoubleTy
),
114 HasBuiltinTyParam(1, LongDoubleTy
))),
115 HasBuiltinTyArg(0, FloatTy
))
119 // Match calls to remquo(double, double, int*) where the first two args are
124 hasName("::remquo"), parameterCountIs(3),
125 HasBuiltinTyParam(0, DoubleTy
), HasBuiltinTyParam(1, DoubleTy
),
126 hasParameter(2, parmVarDecl(hasType(pointerType(
127 pointee(isBuiltinType(IntTy
)))))))),
128 HasBuiltinTyArg(0, FloatTy
), HasBuiltinTyArg(1, FloatTy
))
132 // Match calls to scalbln(double, long) where the first arg is a float.
134 callExpr(callee(functionDecl(hasName("::scalbln"), parameterCountIs(2),
135 HasBuiltinTyParam(0, DoubleTy
),
136 HasBuiltinTyParam(1, LongTy
))),
137 HasBuiltinTyArg(0, FloatTy
))
141 // Match calls to scalbn(double, int) where the first arg is a float.
143 callExpr(callee(functionDecl(hasName("::scalbn"), parameterCountIs(2),
144 HasBuiltinTyParam(0, DoubleTy
),
145 HasBuiltinTyParam(1, IntTy
))),
146 HasBuiltinTyArg(0, FloatTy
))
150 // modf(double, double*) is omitted because the second parameter forces the
151 // type -- there's no conversion from float* to double*.
154 void TypePromotionInMathFnCheck::check(const MatchFinder::MatchResult
&Result
) {
155 const auto *Call
= Result
.Nodes
.getNodeAs
<CallExpr
>("call");
156 assert(Call
!= nullptr);
158 StringRef OldFnName
= Call
->getDirectCallee()->getName();
160 // In C++ mode, we prefer std::foo to ::foof. But some of these suggestions
161 // are only valid in C++11 and newer.
162 static llvm::StringSet
<> Cpp11OnlyFns
= {
163 "acosh", "asinh", "atanh", "cbrt", "copysign", "erf",
164 "erfc", "exp2", "expm1", "fdim", "fma", "fmax",
165 "fmin", "hypot", "ilogb", "lgamma", "llrint", "llround",
166 "log1p", "log2", "logb", "lrint", "lround", "nearbyint",
167 "nextafter", "nexttoward", "remainder", "remquo", "rint", "round",
168 "scalbln", "scalbn", "tgamma", "trunc"};
169 bool StdFnRequiresCpp11
= Cpp11OnlyFns
.count(OldFnName
);
171 std::string NewFnName
;
172 bool FnInCmath
= false;
173 if (getLangOpts().CPlusPlus
&&
174 (!StdFnRequiresCpp11
|| getLangOpts().CPlusPlus11
)) {
175 NewFnName
= ("std::" + OldFnName
).str();
178 NewFnName
= (OldFnName
+ "f").str();
181 auto Diag
= diag(Call
->getExprLoc(), "call to '%0' promotes float to double")
183 << FixItHint::CreateReplacement(
184 Call
->getCallee()->getSourceRange(), NewFnName
);
186 // Suggest including <cmath> if the function we're suggesting is declared in
187 // <cmath> and it's not already included. We never have to suggest including
188 // <math.h>, because the functions we're suggesting moving away from are all
189 // declared in <math.h>.
191 Diag
<< IncludeInserter
.createIncludeInsertion(
192 Result
.Context
->getSourceManager().getFileID(Call
->getBeginLoc()),
196 } // namespace clang::tidy::performance