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