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