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