clang-tools  4.0.0
TypePromotionInMathFnCheck.cpp
Go to the documentation of this file.
1 //===--- TypePromotionInMathFnCheck.cpp - clang-tidy-----------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Frontend/CompilerInstance.h"
14 #include "clang/Lex/Preprocessor.h"
15 #include "llvm/ADT/StringSet.h"
16 
17 using namespace clang::ast_matchers;
18 
19 namespace clang {
20 namespace tidy {
21 namespace performance {
22 
23 namespace {
24 AST_MATCHER_P(Type, isBuiltinType, BuiltinType::Kind, Kind) {
25  if (const auto *BT = dyn_cast<BuiltinType>(&Node)) {
26  return BT->getKind() == Kind;
27  }
28  return false;
29 }
30 } // anonymous namespace
31 
32 TypePromotionInMathFnCheck::TypePromotionInMathFnCheck(
33  StringRef Name, ClangTidyContext *Context)
34  : ClangTidyCheck(Name, Context),
35  IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
36  Options.get("IncludeStyle", "llvm"))) {}
37 
39  CompilerInstance &Compiler) {
40  IncludeInserter = llvm::make_unique<utils::IncludeInserter>(
41  Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle);
42  Compiler.getPreprocessor().addPPCallbacks(
43  IncludeInserter->CreatePPCallbacks());
44 }
45 
48  Options.store(Opts, "IncludeStyle",
49  utils::IncludeSorter::toString(IncludeStyle));
50 }
51 
53  constexpr BuiltinType::Kind IntTy = BuiltinType::Int;
54  constexpr BuiltinType::Kind LongTy = BuiltinType::Long;
55  constexpr BuiltinType::Kind FloatTy = BuiltinType::Float;
56  constexpr BuiltinType::Kind DoubleTy = BuiltinType::Double;
57  constexpr BuiltinType::Kind LongDoubleTy = BuiltinType::LongDouble;
58 
59  auto hasBuiltinTyParam = [](int Pos, BuiltinType::Kind Kind) {
60  return hasParameter(Pos, hasType(isBuiltinType(Kind)));
61  };
62  auto hasBuiltinTyArg = [](int Pos, BuiltinType::Kind Kind) {
63  return hasArgument(Pos, hasType(isBuiltinType(Kind)));
64  };
65 
66  // Match calls to foo(double) with a float argument.
67  auto OneDoubleArgFns = hasAnyName(
68  "::acos", "::acosh", "::asin", "::asinh", "::atan", "::atanh", "::cbrt",
69  "::ceil", "::cos", "::cosh", "::erf", "::erfc", "::exp", "::exp2",
70  "::expm1", "::fabs", "::floor", "::ilogb", "::lgamma", "::llrint",
71  "::log", "::log10", "::log1p", "::log2", "::logb", "::lrint", "::modf",
72  "::nearbyint", "::rint", "::round", "::sin", "::sinh", "::sqrt", "::tan",
73  "::tanh", "::tgamma", "::trunc", "::llround", "::lround");
74  Finder->addMatcher(
75  callExpr(callee(functionDecl(OneDoubleArgFns, parameterCountIs(1),
76  hasBuiltinTyParam(0, DoubleTy))),
77  hasBuiltinTyArg(0, FloatTy))
78  .bind("call"),
79  this);
80 
81  // Match calls to foo(double, double) where both args are floats.
82  auto TwoDoubleArgFns = hasAnyName("::atan2", "::copysign", "::fdim", "::fmax",
83  "::fmin", "::fmod", "::hypot", "::ldexp",
84  "::nextafter", "::pow", "::remainder");
85  Finder->addMatcher(
86  callExpr(callee(functionDecl(TwoDoubleArgFns, parameterCountIs(2),
87  hasBuiltinTyParam(0, DoubleTy),
88  hasBuiltinTyParam(1, DoubleTy))),
89  hasBuiltinTyArg(0, FloatTy), hasBuiltinTyArg(1, FloatTy))
90  .bind("call"),
91  this);
92 
93  // Match calls to fma(double, double, double) where all args are floats.
94  Finder->addMatcher(
95  callExpr(callee(functionDecl(hasName("::fma"), parameterCountIs(3),
96  hasBuiltinTyParam(0, DoubleTy),
97  hasBuiltinTyParam(1, DoubleTy),
98  hasBuiltinTyParam(2, DoubleTy))),
99  hasBuiltinTyArg(0, FloatTy), hasBuiltinTyArg(1, FloatTy),
100  hasBuiltinTyArg(2, FloatTy))
101  .bind("call"),
102  this);
103 
104  // Match calls to frexp(double, int*) where the first arg is a float.
105  Finder->addMatcher(
106  callExpr(callee(functionDecl(
107  hasName("::frexp"), parameterCountIs(2),
108  hasBuiltinTyParam(0, DoubleTy),
109  hasParameter(1, parmVarDecl(hasType(pointerType(
110  pointee(isBuiltinType(IntTy)))))))),
111  hasBuiltinTyArg(0, FloatTy))
112  .bind("call"),
113  this);
114 
115  // Match calls to nexttoward(double, long double) where the first arg is a
116  // float.
117  Finder->addMatcher(
118  callExpr(callee(functionDecl(hasName("::nexttoward"), parameterCountIs(2),
119  hasBuiltinTyParam(0, DoubleTy),
120  hasBuiltinTyParam(1, LongDoubleTy))),
121  hasBuiltinTyArg(0, FloatTy))
122  .bind("call"),
123  this);
124 
125  // Match calls to remquo(double, double, int*) where the first two args are
126  // floats.
127  Finder->addMatcher(
128  callExpr(
129  callee(functionDecl(
130  hasName("::remquo"), parameterCountIs(3),
131  hasBuiltinTyParam(0, DoubleTy), hasBuiltinTyParam(1, DoubleTy),
132  hasParameter(2, parmVarDecl(hasType(pointerType(
133  pointee(isBuiltinType(IntTy)))))))),
134  hasBuiltinTyArg(0, FloatTy), hasBuiltinTyArg(1, FloatTy))
135  .bind("call"),
136  this);
137 
138  // Match calls to scalbln(double, long) where the first arg is a float.
139  Finder->addMatcher(
140  callExpr(callee(functionDecl(hasName("::scalbln"), parameterCountIs(2),
141  hasBuiltinTyParam(0, DoubleTy),
142  hasBuiltinTyParam(1, LongTy))),
143  hasBuiltinTyArg(0, FloatTy))
144  .bind("call"),
145  this);
146 
147  // Match calls to scalbn(double, int) where the first arg is a float.
148  Finder->addMatcher(
149  callExpr(callee(functionDecl(hasName("::scalbn"), parameterCountIs(2),
150  hasBuiltinTyParam(0, DoubleTy),
151  hasBuiltinTyParam(1, IntTy))),
152  hasBuiltinTyArg(0, FloatTy))
153  .bind("call"),
154  this);
155 
156  // modf(double, double*) is omitted because the second parameter forces the
157  // type -- there's no conversion from float* to double*.
158 }
159 
160 void TypePromotionInMathFnCheck::check(const MatchFinder::MatchResult &Result) {
161  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
162  assert(Call != nullptr);
163 
164  StringRef OldFnName = Call->getDirectCallee()->getName();
165 
166  // In C++ mode, we prefer std::foo to ::foof. But some of these suggestions
167  // are only valid in C++11 and newer.
168  static llvm::StringSet<> Cpp11OnlyFns = {
169  "acosh", "asinh", "atanh", "cbrt", "copysign", "erf",
170  "erfc", "exp2", "expm1", "fdim", "fma", "fmax",
171  "fmin", "hypot", "ilogb", "lgamma", "llrint", "llround",
172  "log1p", "log2", "logb", "lrint", "lround", "nearbyint",
173  "nextafter", "nexttoward", "remainder", "remquo", "rint", "round",
174  "scalbln", "scalbn", "tgamma", "trunc"};
175  bool StdFnRequiresCpp11 = Cpp11OnlyFns.count(OldFnName);
176 
177  std::string NewFnName;
178  bool FnInCmath = false;
179  if (getLangOpts().CPlusPlus &&
180  (!StdFnRequiresCpp11 || getLangOpts().CPlusPlus11)) {
181  NewFnName = ("std::" + OldFnName).str();
182  FnInCmath = true;
183  } else {
184  NewFnName = (OldFnName + "f").str();
185  }
186 
187  auto Diag = diag(Call->getExprLoc(), "call to '%0' promotes float to double")
188  << OldFnName
189  << FixItHint::CreateReplacement(
190  Call->getCallee()->getSourceRange(), NewFnName);
191 
192  // Suggest including <cmath> if the function we're suggesting is declared in
193  // <cmath> and it's not already included. We never have to suggest including
194  // <math.h>, because the functions we're suggesting moving away from are all
195  // declared in <math.h>.
196  if (FnInCmath)
197  if (auto IncludeFixit = IncludeInserter->CreateIncludeInsertion(
198  Result.Context->getSourceManager().getFileID(Call->getLocStart()),
199  "cmath", /*IsAngled=*/true))
200  Diag << *IncludeFixit;
201 }
202 
203 } // namespace performance
204 } // namespace tidy
205 } // namespace clang
const std::string Name
Definition: USRFinder.cpp:164
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:262
static StringRef toString(IncludeStyle Style)
Converts IncludeStyle to string representation.
AST_MATCHER_P(CXXForRangeStmt, hasRangeBeginEndStmt, ast_matchers::internal::Matcher< DeclStmt >, InnerMatcher)
void registerPPCallbacks(CompilerInstance &Compiler) override
Override this to register PPCallbacks with Compiler.
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
BindArgumentKind Kind
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.
Definition: ClangTidy.cpp:436
std::map< std::string, std::string > OptionMap
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
ClangTidyContext & Context
Definition: ClangTidy.cpp:87
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.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidy.cpp:403
const NamedDecl * Result
Definition: USRFinder.cpp:162