jlebar updated this revision to Diff 80011.
jlebar added a comment.

Suggest std::foo instead of ::foof.

I spoke with rtrieu IRL and he suggested I do this to match what he did in
SemaChecking.cpp for calls to "abs".  Seems reasonable to me.  This also lets
me sidestep the question of lexing the qualifiers, which is nice.


https://reviews.llvm.org/D27284

Files:
  clang-tools-extra/clang-tidy/performance/CMakeLists.txt
  clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
  clang-tools-extra/clang-tidy/performance/TypePromotionInMathFnCheck.cpp
  clang-tools-extra/clang-tidy/performance/TypePromotionInMathFnCheck.h
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  
clang-tools-extra/docs/clang-tidy/checks/performance-type-promotion-in-math-fn.rst
  clang-tools-extra/test/clang-tidy/performance-type-promotion-in-math-fn.cpp

Index: clang-tools-extra/test/clang-tidy/performance-type-promotion-in-math-fn.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/performance-type-promotion-in-math-fn.cpp
@@ -0,0 +1,314 @@
+// RUN: %check_clang_tidy %s performance-type-promotion-in-math-fn %t
+
+double acos(double);
+double acosh(double);
+double asin(double);
+double asinh(double);
+double atan2(double, double);
+double atan(double);
+double atanh(double);
+double cbrt(double);
+double ceil(double);
+double copysign(double, double);
+double cos(double);
+double cosh(double);
+double erfc(double);
+double erf(double);
+double exp2(double);
+double exp(double);
+double expm1(double);
+double fabs(double);
+double fdim(double, double);
+double floor(double);
+double fma(double, double, double);
+double fmax(double, double);
+double fmin(double, double);
+double fmod(double, double);
+double frexp(double, int *);
+double hypot(double, double);
+double ilogb(double);
+double ldexp(double, double);
+double lgamma(double);
+long long llrint(double);
+double log10(double);
+double log1p(double);
+double log2(double);
+double logb(double);
+double log(double);
+long lrint(double);
+double modf(double);
+double nearbyint(double);
+double nextafter(double, double);
+double nexttoward(double, long double);
+double pow(double, double);
+double remainder(double, double);
+double remquo(double, double, int *);
+double rint(double);
+double round(double);
+double scalbln(double, long);
+double scalbn(double, int);
+double sin(double);
+double sinh(double);
+double sqrt(double);
+double tan(double);
+double tanh(double);
+double tgamma(double);
+double trunc(double);
+long long llround(double);
+long lround(double);
+
+void check_all_fns() {
+  float a, b, c;
+  int i;
+  long l;
+  int *int_ptr;
+
+  acos(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'acos' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  std::acos(a);{{$}}
+  acosh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'acosh'
+  // CHECK-FIXES: {{^}}  std::acosh(a);{{$}}
+  asin(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'asin'
+  // CHECK-FIXES: {{^}}  std::asin(a);{{$}}
+  asinh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'asinh'
+  // CHECK-FIXES: {{^}}  std::asinh(a);{{$}}
+  atan2(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'atan2'
+  // CHECK-FIXES: {{^}}  std::atan2(a, b);{{$}}
+  atan(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'atan'
+  // CHECK-FIXES: {{^}}  std::atan(a);{{$}}
+  atanh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'atanh'
+  // CHECK-FIXES: {{^}}  std::atanh(a);{{$}}
+  cbrt(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'cbrt'
+  // CHECK-FIXES: {{^}}  std::cbrt(a);{{$}}
+  ceil(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'ceil'
+  // CHECK-FIXES: {{^}}  std::ceil(a);{{$}}
+  copysign(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'copysign'
+  // CHECK-FIXES: {{^}}  std::copysign(a, b);{{$}}
+  cos(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'cos'
+  // CHECK-FIXES: {{^}}  std::cos(a);{{$}}
+  cosh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'cosh'
+  // CHECK-FIXES: {{^}}  std::cosh(a);{{$}}
+  erf(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'erf'
+  // CHECK-FIXES: {{^}}  std::erf(a);{{$}}
+  erfc(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'erfc'
+  // CHECK-FIXES: {{^}}  std::erfc(a);{{$}}
+  exp2(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'exp2'
+  // CHECK-FIXES: {{^}}  std::exp2(a);{{$}}
+  exp(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'exp'
+  // CHECK-FIXES: {{^}}  std::exp(a);{{$}}
+  expm1(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'expm1'
+  // CHECK-FIXES: {{^}}  std::expm1(a);{{$}}
+  fabs(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fabs'
+  // CHECK-FIXES: {{^}}  std::fabs(a);{{$}}
+  fdim(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fdim'
+  // CHECK-FIXES: {{^}}  std::fdim(a, b);{{$}}
+  floor(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'floor'
+  // CHECK-FIXES: {{^}}  std::floor(a);{{$}}
+  fma(a, b, c);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fma'
+  // CHECK-FIXES: {{^}}  std::fma(a, b, c);{{$}}
+  fmax(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fmax'
+  // CHECK-FIXES: {{^}}  std::fmax(a, b);{{$}}
+  fmin(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fmin'
+  // CHECK-FIXES: {{^}}  std::fmin(a, b);{{$}}
+  fmod(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fmod'
+  // CHECK-FIXES: {{^}}  std::fmod(a, b);{{$}}
+  frexp(a, int_ptr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'frexp'
+  // CHECK-FIXES: {{^}}  std::frexp(a, int_ptr);{{$}}
+  hypot(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'hypot'
+  // CHECK-FIXES: {{^}}  std::hypot(a, b);{{$}}
+  ilogb(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'ilogb'
+  // CHECK-FIXES: {{^}}  std::ilogb(a);{{$}}
+  ldexp(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'ldexp'
+  // CHECK-FIXES: {{^}}  std::ldexp(a, b);{{$}}
+  lgamma(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'lgamma'
+  // CHECK-FIXES: {{^}}  std::lgamma(a);{{$}}
+  llrint(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'llrint'
+  // CHECK-FIXES: {{^}}  std::llrint(a);{{$}}
+  llround(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'llround'
+  // CHECK-FIXES: {{^}}  std::llround(a);{{$}}
+  log10(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log10'
+  // CHECK-FIXES: {{^}}  std::log10(a);{{$}}
+  log1p(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log1p'
+  // CHECK-FIXES: {{^}}  std::log1p(a);{{$}}
+  log2(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log2'
+  // CHECK-FIXES: {{^}}  std::log2(a);{{$}}
+  log(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log'
+  // CHECK-FIXES: {{^}}  std::log(a);{{$}}
+  logb(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'logb'
+  // CHECK-FIXES: {{^}}  std::logb(a);{{$}}
+  lrint(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'lrint'
+  // CHECK-FIXES: {{^}}  std::lrint(a);{{$}}
+  lround(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'lround'
+  // CHECK-FIXES: {{^}}  std::lround(a);{{$}}
+  nearbyint(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nearbyint'
+  // CHECK-FIXES: {{^}}  std::nearbyint(a);{{$}}
+  nextafter(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nextafter'
+  // CHECK-FIXES: {{^}}  std::nextafter(a, b);{{$}}
+  nexttoward(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward'
+  // CHECK-FIXES: {{^}}  std::nexttoward(a, b);{{$}}
+  pow(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'pow'
+  // CHECK-FIXES: {{^}}  std::pow(a, b);{{$}}
+  remainder(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'remainder'
+  // CHECK-FIXES: {{^}}  std::remainder(a, b);{{$}}
+  remquo(a, b, int_ptr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'remquo'
+  // CHECK-FIXES: {{^}}  std::remquo(a, b, int_ptr);{{$}}
+  rint(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'rint'
+  // CHECK-FIXES: {{^}}  std::rint(a);{{$}}
+  round(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'round'
+  // CHECK-FIXES: {{^}}  std::round(a);{{$}}
+  scalbln(a, l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbln'
+  // CHECK-FIXES: {{^}}  std::scalbln(a, l);{{$}}
+  scalbn(a, i);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbn'
+  // CHECK-FIXES: {{^}}  std::scalbn(a, i);{{$}}
+  sin(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sin'
+  // CHECK-FIXES: {{^}}  std::sin(a);{{$}}
+  sinh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sinh'
+  // CHECK-FIXES: {{^}}  std::sinh(a);{{$}}
+  sqrt(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sqrt'
+  // CHECK-FIXES: {{^}}  std::sqrt(a);{{$}}
+  tan(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'tan'
+  // CHECK-FIXES: {{^}}  std::tan(a);{{$}}
+  tanh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'tanh'
+  // CHECK-FIXES: {{^}}  std::tanh(a);{{$}}
+  tgamma(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'tgamma'
+  // CHECK-FIXES: {{^}}  std::tgamma(a);{{$}}
+  trunc(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'trunc'
+  // CHECK-FIXES: {{^}}  std::trunc(a);{{$}}
+}
+
+// nexttoward/nexttowardf are weird -- the second param is always long double.
+// So we warn if the first arg is a float, regardless of what the second arg is.
+void check_nexttoward() {
+  nexttoward(0.f, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward'
+  // CHECK-FIXES: {{^}}  std::nexttoward(0.f, 0);{{$}}
+  nexttoward(0.f, 0l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward'
+  // CHECK-FIXES: {{^}}  std::nexttoward(0.f, 0l);{{$}}
+  nexttoward(0.f, 0.f);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward'
+  // CHECK-FIXES: {{^}}  std::nexttoward(0.f, 0.f);{{$}}
+  nexttoward(0.f, 0.);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward'
+  // CHECK-FIXES: {{^}}  std::nexttoward(0.f, 0.);{{$}}
+
+  // No warnings for these.
+  nexttoward(0., 0);
+  nexttoward(0., 0.f);
+  nexttoward(0., 0.);
+}
+
+// The second parameter to scalbn and scalbnf is an int, so we don't care what
+// type you pass as that argument; we warn iff the first argument is a float.
+void check_scalbn() {
+  scalbn(0.f, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbn'
+  // CHECK-FIXES: {{^}}  std::scalbn(0.f, 0);{{$}}
+  scalbn(0.f, static_cast<char>(0));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbn'
+  // CHECK-FIXES: {{^}}  std::scalbn(0.f, static_cast<char>(0));{{$}}
+
+  // No warnings for these.
+  scalbn(0., 0);
+  scalbn(0., static_cast<char>(0));
+}
+
+// scalbln/scalblnf are like scalbn/scalbnf except their second arg is a long.
+// Again, doesn't matter what we pass for the second arg; we warn iff the first
+// arg is a float.
+void check_scalbln() {
+  scalbln(0.f, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbln'
+  // CHECK-FIXES: {{^}}  std::scalbln(0.f, 0);{{$}}
+  scalbln(0.f, 0l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbln'
+  // CHECK-FIXES: {{^}}  std::scalbln(0.f, 0l);{{$}}
+
+  // No warnings for these.
+  scalbln(0., 0);
+  scalbln(0., 0l);
+}
+
+float cosf(float);
+double foo(double);         // not a math.h function
+float cos(float);           // not a math.h function (wrong signature)
+double cos(double, double); // not a math.h function (wrong signature)
+
+namespace std {
+void cos(float);
+} // namespace std
+
+void check_no_warnings() {
+  foo(0.); // no warning because not a math.h function.
+
+  sin(0);        // no warning because arg is an int
+  cos(0.);       // no warning because arg is a double
+  std::cos(0.f); // no warning because not ::cos.
+  cosf(0.f);     // no warning; we expect this to take a float
+  cos(0.f);      // does not match the expected signature of ::cos
+  cos(0.f, 0.f); // does not match the expected signature of ::cos
+
+  // No warnings because all args are not floats.
+  remainder(0., 0.f);
+  remainder(0.f, 0.);
+  remainder(0, 0.f);
+  remainder(0.f, 0);
+  fma(0.f, 0.f, 0);
+  fma(0.f, 0.f, 0.);
+  fma(0.f, 0., 0.f);
+  fma(0., 0.f, 0.f);
+}
Index: clang-tools-extra/docs/clang-tidy/checks/performance-type-promotion-in-math-fn.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/performance-type-promotion-in-math-fn.rst
@@ -0,0 +1,11 @@
+.. title:: clang-tidy - performance-type-promotion-in-math-fn
+
+performance-type-promotion-in-math-fn
+=====================================
+
+Finds calls to C math library functions (from ``math.h`` or, in C++, ``cmath``)
+with implicit ``float`` to ``double`` promotions.
+
+For example, warns on ``::sin(0.f)``, because this funciton's parameter is a
+double. You probably meant to call ``std::sin(0.f)`` (in C++), or ``sinf(0.f)``
+(in C).
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -122,6 +122,7 @@
    performance-for-range-copy
    performance-implicit-cast-in-loop
    performance-inefficient-string-concatenation
+   performance-type-promotion-in-math-fn
    performance-unnecessary-copy-initialization
    performance-unnecessary-value-param
    readability-avoid-const-params-in-decls
Index: clang-tools-extra/clang-tidy/performance/TypePromotionInMathFnCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/performance/TypePromotionInMathFnCheck.h
@@ -0,0 +1,40 @@
+//===--- TypePromotionInMathFnCheck.h - clang-tidy---------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_TYPE_PROMOTION_IN_MATH_FN_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_TYPE_PROMOTION_IN_MATH_FN_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+/// Finds calls to C math library functions with implicit float to double
+/// promotions.
+///
+/// For example, warns on ::sin(0.f), because this funciton's parameter is a
+/// double.  You probably meant to call std::sin(0.f) (in C++), or sinf(0.f) (in
+/// C).
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/performance-type-promotion-in-math-fn.html
+class TypePromotionInMathFnCheck : public ClangTidyCheck {
+public:
+  TypePromotionInMathFnCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_TYPE_PROMOTION_IN_MATH_FN_H
Index: clang-tools-extra/clang-tidy/performance/TypePromotionInMathFnCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/performance/TypePromotionInMathFnCheck.cpp
@@ -0,0 +1,173 @@
+//===--- TypePromotionInMathFnCheck.cpp - clang-tidy-----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TypePromotionInMathFnCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.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
+
+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.
+  bool StdFnRequresCpp11 =
+      llvm::StringSwitch<bool>(OldFnName)
+          .Cases("acosh", "asinh", "atanh", "cbrt", "copysign", "erf", "erfc",
+                 "exp2", "expm1", "fdim", true)
+          .Cases("fma", "fmax", "fmin", "hypot", "ilogb", "lgamma", "llrint",
+                 "llround", "log1p", true)
+          .Cases("log2", "logb", "lrint", "lround", "nearbyint", "nextafter",
+                 "nexttoward", "remainder", "remquo", "rint", true)
+          .Cases("round", "scalbln", "scalbn", "tgamma", "trunc", true)
+          .Default(false);
+
+  std::string NewFnName;
+  if (getLangOpts().CPlusPlus &&
+      (!StdFnRequresCpp11 || getLangOpts().CPlusPlus11))
+    NewFnName = ("std::" + OldFnName).str();
+  else
+    NewFnName = (OldFnName + "f").str();
+
+  diag(Call->getExprLoc(), "call to '%0' promotes float to double")
+      << OldFnName << FixItHint::CreateReplacement(
+                          Call->getCallee()->getSourceRange(), NewFnName);
+
+  // FIXME: Perhaps we should suggest #include <cmath> if we suggest a cmath
+  // function and cmath is not already included.
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
+++ clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
@@ -10,11 +10,11 @@
 #include "../ClangTidy.h"
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
-#include "InefficientStringConcatenationCheck.h"
-
 #include "FasterStringFindCheck.h"
 #include "ForRangeCopyCheck.h"
 #include "ImplicitCastInLoopCheck.h"
+#include "InefficientStringConcatenationCheck.h"
+#include "TypePromotionInMathFnCheck.h"
 #include "UnnecessaryCopyInitialization.h"
 #include "UnnecessaryValueParamCheck.h"
 
@@ -33,6 +33,8 @@
         "performance-implicit-cast-in-loop");
     CheckFactories.registerCheck<InefficientStringConcatenationCheck>(
         "performance-inefficient-string-concatenation");
+    CheckFactories.registerCheck<TypePromotionInMathFnCheck>(
+        "performance-type-promotion-in-math-fn");
     CheckFactories.registerCheck<UnnecessaryCopyInitialization>(
         "performance-unnecessary-copy-initialization");
     CheckFactories.registerCheck<UnnecessaryValueParamCheck>(
Index: clang-tools-extra/clang-tidy/performance/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/performance/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/performance/CMakeLists.txt
@@ -6,6 +6,7 @@
   ImplicitCastInLoopCheck.cpp
   InefficientStringConcatenationCheck.cpp
   PerformanceTidyModule.cpp
+  TypePromotionInMathFnCheck.cpp
   UnnecessaryCopyInitialization.cpp
   UnnecessaryValueParamCheck.cpp
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to