clang-tools 22.0.0git
TypePromotionInMathFnCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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 return false;
25}
26} // anonymous namespace
27
29 StringRef Name, ClangTidyContext *Context)
30 : ClangTidyCheck(Name, Context),
31 IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
32 utils::IncludeSorter::IS_LLVM),
33 areDiagsSelfContained()) {}
34
36 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
37 IncludeInserter.registerPreprocessor(PP);
38}
39
42 Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
43}
44
46 constexpr BuiltinType::Kind IntTy = BuiltinType::Int;
47 constexpr BuiltinType::Kind LongTy = BuiltinType::Long;
48 constexpr BuiltinType::Kind FloatTy = BuiltinType::Float;
49 constexpr BuiltinType::Kind DoubleTy = BuiltinType::Double;
50 constexpr BuiltinType::Kind LongDoubleTy = BuiltinType::LongDouble;
51
52 auto HasBuiltinTyParam = [](int Pos, BuiltinType::Kind Kind) {
53 return hasParameter(Pos, hasType(isBuiltinType(Kind)));
54 };
55 auto HasBuiltinTyArg = [](int Pos, BuiltinType::Kind Kind) {
56 return hasArgument(Pos, hasType(isBuiltinType(Kind)));
57 };
58
59 // Match calls to foo(double) with a float argument.
60 auto OneDoubleArgFns = hasAnyName(
61 "::acos", "::acosh", "::asin", "::asinh", "::atan", "::atanh", "::cbrt",
62 "::ceil", "::cos", "::cosh", "::erf", "::erfc", "::exp", "::exp2",
63 "::expm1", "::fabs", "::floor", "::ilogb", "::lgamma", "::llrint",
64 "::log", "::log10", "::log1p", "::log2", "::logb", "::lrint", "::modf",
65 "::nearbyint", "::rint", "::round", "::sin", "::sinh", "::sqrt", "::tan",
66 "::tanh", "::tgamma", "::trunc", "::llround", "::lround");
67 Finder->addMatcher(
68 callExpr(callee(functionDecl(OneDoubleArgFns, parameterCountIs(1),
69 HasBuiltinTyParam(0, DoubleTy))),
70 HasBuiltinTyArg(0, FloatTy))
71 .bind("call"),
72 this);
73
74 // Match calls to foo(double, double) where both args are floats.
75 auto TwoDoubleArgFns = hasAnyName("::atan2", "::copysign", "::fdim", "::fmax",
76 "::fmin", "::fmod", "::hypot", "::ldexp",
77 "::nextafter", "::pow", "::remainder");
78 Finder->addMatcher(
79 callExpr(callee(functionDecl(TwoDoubleArgFns, parameterCountIs(2),
80 HasBuiltinTyParam(0, DoubleTy),
81 HasBuiltinTyParam(1, DoubleTy))),
82 HasBuiltinTyArg(0, FloatTy), HasBuiltinTyArg(1, FloatTy))
83 .bind("call"),
84 this);
85
86 // Match calls to fma(double, double, double) where all args are floats.
87 Finder->addMatcher(
88 callExpr(callee(functionDecl(hasName("::fma"), parameterCountIs(3),
89 HasBuiltinTyParam(0, DoubleTy),
90 HasBuiltinTyParam(1, DoubleTy),
91 HasBuiltinTyParam(2, DoubleTy))),
92 HasBuiltinTyArg(0, FloatTy), HasBuiltinTyArg(1, FloatTy),
93 HasBuiltinTyArg(2, FloatTy))
94 .bind("call"),
95 this);
96
97 // Match calls to frexp(double, int*) where the first arg is a float.
98 Finder->addMatcher(
99 callExpr(callee(functionDecl(
100 hasName("::frexp"), parameterCountIs(2),
101 HasBuiltinTyParam(0, DoubleTy),
102 hasParameter(1, parmVarDecl(hasType(pointerType(
103 pointee(isBuiltinType(IntTy)))))))),
104 HasBuiltinTyArg(0, FloatTy))
105 .bind("call"),
106 this);
107
108 // Match calls to nexttoward(double, long double) where the first arg is a
109 // float.
110 Finder->addMatcher(
111 callExpr(callee(functionDecl(hasName("::nexttoward"), parameterCountIs(2),
112 HasBuiltinTyParam(0, DoubleTy),
113 HasBuiltinTyParam(1, LongDoubleTy))),
114 HasBuiltinTyArg(0, FloatTy))
115 .bind("call"),
116 this);
117
118 // Match calls to remquo(double, double, int*) where the first two args are
119 // floats.
120 Finder->addMatcher(
121 callExpr(
122 callee(functionDecl(
123 hasName("::remquo"), parameterCountIs(3),
124 HasBuiltinTyParam(0, DoubleTy), HasBuiltinTyParam(1, DoubleTy),
125 hasParameter(2, parmVarDecl(hasType(pointerType(
126 pointee(isBuiltinType(IntTy)))))))),
127 HasBuiltinTyArg(0, FloatTy), HasBuiltinTyArg(1, FloatTy))
128 .bind("call"),
129 this);
130
131 // Match calls to scalbln(double, long) where the first arg is a float.
132 Finder->addMatcher(
133 callExpr(callee(functionDecl(hasName("::scalbln"), parameterCountIs(2),
134 HasBuiltinTyParam(0, DoubleTy),
135 HasBuiltinTyParam(1, LongTy))),
136 HasBuiltinTyArg(0, FloatTy))
137 .bind("call"),
138 this);
139
140 // Match calls to scalbn(double, int) where the first arg is a float.
141 Finder->addMatcher(
142 callExpr(callee(functionDecl(hasName("::scalbn"), parameterCountIs(2),
143 HasBuiltinTyParam(0, DoubleTy),
144 HasBuiltinTyParam(1, IntTy))),
145 HasBuiltinTyArg(0, FloatTy))
146 .bind("call"),
147 this);
148
149 // modf(double, double*) is omitted because the second parameter forces the
150 // type -- there's no conversion from float* to double*.
151}
152
153void TypePromotionInMathFnCheck::check(const MatchFinder::MatchResult &Result) {
154 const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
155 assert(Call != nullptr);
156
157 const StringRef OldFnName = Call->getDirectCallee()->getName();
158
159 // In C++ mode, we prefer std::foo to ::foof. But some of these suggestions
160 // are only valid in C++11 and newer.
161 static const llvm::StringSet<> Cpp11OnlyFns = {
162 "acosh", "asinh", "atanh", "cbrt", "copysign", "erf",
163 "erfc", "exp2", "expm1", "fdim", "fma", "fmax",
164 "fmin", "hypot", "ilogb", "lgamma", "llrint", "llround",
165 "log1p", "log2", "logb", "lrint", "lround", "nearbyint",
166 "nextafter", "nexttoward", "remainder", "remquo", "rint", "round",
167 "scalbln", "scalbn", "tgamma", "trunc"};
168 const bool StdFnRequiresCpp11 = Cpp11OnlyFns.contains(OldFnName);
169
170 std::string NewFnName;
171 bool FnInCmath = false;
172 if (getLangOpts().CPlusPlus &&
173 (!StdFnRequiresCpp11 || getLangOpts().CPlusPlus11)) {
174 NewFnName = ("std::" + OldFnName).str();
175 FnInCmath = true;
176 } else {
177 NewFnName = (OldFnName + "f").str();
178 }
179
180 auto Diag = diag(Call->getExprLoc(), "call to '%0' promotes float to double")
181 << OldFnName
182 << FixItHint::CreateReplacement(
183 Call->getCallee()->getSourceRange(), NewFnName);
184
185 // Suggest including <cmath> if the function we're suggesting is declared in
186 // <cmath> and it's not already included. We never have to suggest including
187 // <math.h>, because the functions we're suggesting moving away from are all
188 // declared in <math.h>.
189 if (FnInCmath)
190 Diag << IncludeInserter.createIncludeInsertion(
191 Result.Context->getSourceManager().getFileID(Call->getBeginLoc()),
192 "<cmath>");
193}
194
195} // namespace clang::tidy::performance
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
TypePromotionInMathFnCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
llvm::StringMap< ClangTidyValue > OptionMap