clang-tools 19.0.0git
TypePromotionInMathFnCheck.cpp
Go to the documentation of this file.
1//===--- TypePromotionInMathFnCheck.cpp - clang-tidy-----------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
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"
15
16using namespace clang::ast_matchers;
17
19
20namespace {
21AST_MATCHER_P(Type, isBuiltinType, BuiltinType::Kind, Kind) {
22 if (const auto *BT = dyn_cast<BuiltinType>(&Node)) {
23 return BT->getKind() == Kind;
24 }
25 return false;
26}
27} // anonymous namespace
28
30 StringRef Name, ClangTidyContext *Context)
31 : ClangTidyCheck(Name, Context),
32 IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
33 utils::IncludeSorter::IS_LLVM),
34 areDiagsSelfContained()) {}
35
37 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
38 IncludeInserter.registerPreprocessor(PP);
39}
40
43 Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
44}
45
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;
52
53 auto HasBuiltinTyParam = [](int Pos, BuiltinType::Kind Kind) {
54 return hasParameter(Pos, hasType(isBuiltinType(Kind)));
55 };
56 auto HasBuiltinTyArg = [](int Pos, BuiltinType::Kind Kind) {
57 return hasArgument(Pos, hasType(isBuiltinType(Kind)));
58 };
59
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");
68 Finder->addMatcher(
69 callExpr(callee(functionDecl(OneDoubleArgFns, parameterCountIs(1),
70 HasBuiltinTyParam(0, DoubleTy))),
71 HasBuiltinTyArg(0, FloatTy))
72 .bind("call"),
73 this);
74
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");
79 Finder->addMatcher(
80 callExpr(callee(functionDecl(TwoDoubleArgFns, parameterCountIs(2),
81 HasBuiltinTyParam(0, DoubleTy),
82 HasBuiltinTyParam(1, DoubleTy))),
83 HasBuiltinTyArg(0, FloatTy), HasBuiltinTyArg(1, FloatTy))
84 .bind("call"),
85 this);
86
87 // Match calls to fma(double, double, double) where all args are floats.
88 Finder->addMatcher(
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))
95 .bind("call"),
96 this);
97
98 // Match calls to frexp(double, int*) where the first arg is a float.
99 Finder->addMatcher(
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))
106 .bind("call"),
107 this);
108
109 // Match calls to nexttoward(double, long double) where the first arg is a
110 // float.
111 Finder->addMatcher(
112 callExpr(callee(functionDecl(hasName("::nexttoward"), parameterCountIs(2),
113 HasBuiltinTyParam(0, DoubleTy),
114 HasBuiltinTyParam(1, LongDoubleTy))),
115 HasBuiltinTyArg(0, FloatTy))
116 .bind("call"),
117 this);
118
119 // Match calls to remquo(double, double, int*) where the first two args are
120 // floats.
121 Finder->addMatcher(
122 callExpr(
123 callee(functionDecl(
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))
129 .bind("call"),
130 this);
131
132 // Match calls to scalbln(double, long) where the first arg is a float.
133 Finder->addMatcher(
134 callExpr(callee(functionDecl(hasName("::scalbln"), parameterCountIs(2),
135 HasBuiltinTyParam(0, DoubleTy),
136 HasBuiltinTyParam(1, LongTy))),
137 HasBuiltinTyArg(0, FloatTy))
138 .bind("call"),
139 this);
140
141 // Match calls to scalbn(double, int) where the first arg is a float.
142 Finder->addMatcher(
143 callExpr(callee(functionDecl(hasName("::scalbn"), parameterCountIs(2),
144 HasBuiltinTyParam(0, DoubleTy),
145 HasBuiltinTyParam(1, IntTy))),
146 HasBuiltinTyArg(0, FloatTy))
147 .bind("call"),
148 this);
149
150 // modf(double, double*) is omitted because the second parameter forces the
151 // type -- there's no conversion from float* to double*.
152}
153
154void TypePromotionInMathFnCheck::check(const MatchFinder::MatchResult &Result) {
155 const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
156 assert(Call != nullptr);
157
158 StringRef OldFnName = Call->getDirectCallee()->getName();
159
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);
170
171 std::string NewFnName;
172 bool FnInCmath = false;
173 if (getLangOpts().CPlusPlus &&
174 (!StdFnRequiresCpp11 || getLangOpts().CPlusPlus11)) {
175 NewFnName = ("std::" + OldFnName).str();
176 FnInCmath = true;
177 } else {
178 NewFnName = (OldFnName + "f").str();
179 }
180
181 auto Diag = diag(Call->getExprLoc(), "call to '%0' promotes float to double")
182 << OldFnName
183 << FixItHint::CreateReplacement(
184 Call->getCallee()->getSourceRange(), NewFnName);
185
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>.
190 if (FnInCmath)
191 Diag << IncludeInserter.createIncludeInsertion(
192 Result.Context->getSourceManager().getFileID(Call->getBeginLoc()),
193 "<cmath>");
194}
195
196} // namespace clang::tidy::performance
BindArgumentKind Kind
llvm::SmallString< 256U > Name
NodeType Type
size_t Pos
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.
const LangOptions & getLangOpts() const
Returns the language options from the context.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
TypePromotionInMathFnCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
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)
llvm::StringMap< ClangTidyValue > OptionMap