jlebar created this revision.
jlebar added a subscriber: cfe-commits.
Herald added a subscriber: mgorny.

This checks for calls to double-precision math.h with single-precision
arguments.  For example, it suggests replacing ::sin(0.f) with
::sinf(0.f).


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,321 @@
+// 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 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: {{^}}  acosf(a);{{$}}
+  acosh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'acosh' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  acoshf(a);{{$}}
+  asin(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'asin' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  asinf(a);{{$}}
+  asinh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'asinh' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  asinhf(a);{{$}}
+  atan2(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'atan2' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  atan2f(a);{{$}}
+  atan(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'atan' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  atanf(a);{{$}}
+  atanh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'atanh' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  atanhf(a);{{$}}
+  cbrt(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'cbrt' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  cbrtf(a);{{$}}
+  ceil(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'ceil' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  ceilf(a);{{$}}
+  copysign(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'copysign' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  copysignf(a, b);{{$}}
+  cos(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'cos' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  cosf(a);{{$}}
+  cosh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'cosh' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  coshf(a);{{$}}
+  erf(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'erf' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  erff(a);{{$}}
+  erfc(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'erfc' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  erfcf(a);{{$}}
+  exp2(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'exp2' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  exp2f(a);{{$}}
+  exp(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'exp' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  expf(a);{{$}}
+  expm1(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'expm1' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  expm1f(a);{{$}}
+  fabs(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fabs' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  fabsf(a);{{$}}
+  fdim(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fdim' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  fdimf(a, b);{{$}}
+  floor(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'floor' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  floorf(a);{{$}}
+  fma(a, b, c);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fma' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  fmaf(a, b, c);{{$}}
+  fmax(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fmax' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  fmaxf(a, b);{{$}}
+  fmin(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fmin' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  fminf(a, b);{{$}}
+  fmod(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fmod' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  fmodf(a, b);{{$}}
+  frexp(a, int_ptr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'frexp' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  frexpf(a, int_ptr);{{$}}
+  hypot(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'hypot' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  hypotf(a, b);{{$}}
+  ilogb(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'ilogb' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  ilogbf(a);{{$}}
+  ldexp(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'ldexp' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  ldexpf(a, b);{{$}}
+  lgamma(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'lgamma' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  lgammaf(a);{{$}}
+  llrint(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'llrint' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  llrintf(a);{{$}}
+  llround(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'llround' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  llroundf(a);{{$}}
+  log10(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log10' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  log10f(a);{{$}}
+  log1p(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log1p' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  log1pf(a);{{$}}
+  log2(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log2' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  log2f(a);{{$}}
+  log(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  logf(a);{{$}}
+  logb(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'logb' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  logbf(a);{{$}}
+  lrint(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'lrint' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  lrintf(a);{{$}}
+  lround(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'lround' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  lroundf(a);{{$}}
+  nearbyint(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nearbyint' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  nearbyintf(a);{{$}}
+  nextafter(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nextafter' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  nextafterf(a, b);{{$}}
+  nexttoward(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  nexttowardf(a, b);{{$}}
+  pow(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'pow' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  powf(a, b);{{$}}
+  remainder(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'remainder' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  remainderf(a, b);{{$}}
+  remquo(a, b, int_ptr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'remquo' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  remquof(a, b, int_ptr);{{$}}
+  rint(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'rint' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  rintf(a);{{$}}
+  round(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'round' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  roundf(a);{{$}}
+  scalbln(a, l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbln' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  scalblnf(a, l);{{$}}
+  scalbn(a, i);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbn' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  scalbnf(a, i);{{$}}
+  sin(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sin' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  sinf(a);{{$}}
+  sinh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sinh' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  sinhf(a);{{$}}
+  sqrt(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sqrt' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  sqrtf(a);{{$}}
+  tan(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'tan' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  tanf(a);{{$}}
+  tanh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'tanh' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  tanhf(a);{{$}}
+  tgamma(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'tgamma' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  tgammaf(a);{{$}}
+  trunc(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'trunc' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  truncf(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' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  nexttowardf(0.f, 0);{{$}}
+  nexttoward(0.f, 0l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  nexttowardf(0.f, 0l);{{$}}
+  nexttoward(0.f, 0.f);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  nexttowardf(0.f, 0.f);{{$}}
+  nexttoward(0.f, 0.);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  nexttowardf(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' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  scalbnf(0.f, 0);{{$}}
+  scalbn(0.f, static_cast<char>(0));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbn' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  scalbnf(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' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  scalblnf(0.f, 0);{{$}}
+  scalbln(0.f, 0l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbln' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  scalblnf(0.f, 0l);{{$}}
+
+  // No warnings for these.
+  scalbln(0., 0);
+  scalbln(0., 0l);
+}
+
+// Check that we preserve the leading "::" in our fixit.
+void fixit_preserves_qualifier() {
+  ::sin(0.f);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sin' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  ::sinf(0.f);{{$}}
+}
+
+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,9 @@
+.. title:: clang-tidy - performance-type-promotion-in-math-fn
+
+performance-type-promotion-in-math-fn
+=====================================
+
+Finds calls to math.h 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 `::sinf(0.f)`, or maybe `std::sin(0.f)`.
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,38 @@
+//===--- 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 math.h 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 ::sinf(0.f).
+///
+/// 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,159 @@
+//===--- 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 BuiltinType *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", "::atan2", "::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("::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);
+
+  SourceLocation FnNameStart = Call->getExprLoc();
+  if (auto *D = dyn_cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit()))
+    if (D->hasQualifier()) {
+      // Skip the "::" following the qualifier.
+      FnNameStart = D->getQualifierLoc().getEndLoc().getLocWithOffset(2);
+    }
+
+  auto FnNameSourceRange = CharSourceRange::getCharRange(
+      FnNameStart, Call->getArg(0)->getExprLoc().getLocWithOffset(-1));
+
+  StringRef FnName = Call->getDirectCallee()->getName();
+  diag(Call->getExprLoc(), "call to '%0' promotes float to double")
+      << FnName << FixItHint::CreateReplacement(
+                       FnNameSourceRange, (llvm::Twine(FnName) + "f").str());
+}
+
+} // 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