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