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 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( "performance-inefficient-string-concatenation"); + CheckFactories.registerCheck( + "performance-type-promotion-in-math-fn"); CheckFactories.registerCheck( "performance-unnecessary-copy-initialization"); CheckFactories.registerCheck( Index: clang-tools-extra/clang-tidy/performance/TypePromotionInMathFnCheck.h =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/performance/TypePromotionInMathFnCheck.h @@ -0,0 +1,39 @@ +//===--- 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 ::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 auto *BT = dyn_cast(&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("call"); + assert(Call != nullptr); + + SourceLocation FnNameStart = Call->getExprLoc(); + if (auto *D = dyn_cast(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/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/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,10 @@ +.. 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 ``::sinf(0.f)``, or maybe ``std::sin(0.f)``. 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,326 @@ +// 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' + // CHECK-FIXES: {{^}} acoshf(a);{{$}} + asin(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'asin' + // CHECK-FIXES: {{^}} asinf(a);{{$}} + asinh(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'asinh' + // CHECK-FIXES: {{^}} asinhf(a);{{$}} + atan2(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'atan2' + // CHECK-FIXES: {{^}} atan2f(a);{{$}} + atan(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'atan' + // CHECK-FIXES: {{^}} atanf(a);{{$}} + atanh(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'atanh' + // CHECK-FIXES: {{^}} atanhf(a);{{$}} + cbrt(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'cbrt' + // CHECK-FIXES: {{^}} cbrtf(a);{{$}} + ceil(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'ceil' + // CHECK-FIXES: {{^}} ceilf(a);{{$}} + copysign(a, b); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'copysign' + // CHECK-FIXES: {{^}} copysignf(a, b);{{$}} + cos(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'cos' + // CHECK-FIXES: {{^}} cosf(a);{{$}} + cosh(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'cosh' + // CHECK-FIXES: {{^}} coshf(a);{{$}} + erf(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'erf' + // CHECK-FIXES: {{^}} erff(a);{{$}} + erfc(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'erfc' + // CHECK-FIXES: {{^}} erfcf(a);{{$}} + exp2(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'exp2' + // CHECK-FIXES: {{^}} exp2f(a);{{$}} + exp(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'exp' + // CHECK-FIXES: {{^}} expf(a);{{$}} + expm1(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'expm1' + // CHECK-FIXES: {{^}} expm1f(a);{{$}} + fabs(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fabs' + // CHECK-FIXES: {{^}} fabsf(a);{{$}} + fdim(a, b); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fdim' + // CHECK-FIXES: {{^}} fdimf(a, b);{{$}} + floor(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'floor' + // CHECK-FIXES: {{^}} floorf(a);{{$}} + fma(a, b, c); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fma' + // CHECK-FIXES: {{^}} fmaf(a, b, c);{{$}} + fmax(a, b); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fmax' + // CHECK-FIXES: {{^}} fmaxf(a, b);{{$}} + fmin(a, b); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fmin' + // CHECK-FIXES: {{^}} fminf(a, b);{{$}} + fmod(a, b); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fmod' + // CHECK-FIXES: {{^}} fmodf(a, b);{{$}} + frexp(a, int_ptr); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'frexp' + // CHECK-FIXES: {{^}} frexpf(a, int_ptr);{{$}} + hypot(a, b); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'hypot' + // CHECK-FIXES: {{^}} hypotf(a, b);{{$}} + ilogb(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'ilogb' + // CHECK-FIXES: {{^}} ilogbf(a);{{$}} + ldexp(a, b); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'ldexp' + // CHECK-FIXES: {{^}} ldexpf(a, b);{{$}} + lgamma(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'lgamma' + // CHECK-FIXES: {{^}} lgammaf(a);{{$}} + llrint(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'llrint' + // CHECK-FIXES: {{^}} llrintf(a);{{$}} + llround(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'llround' + // CHECK-FIXES: {{^}} llroundf(a);{{$}} + log10(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log10' + // CHECK-FIXES: {{^}} log10f(a);{{$}} + log1p(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log1p' + // CHECK-FIXES: {{^}} log1pf(a);{{$}} + log2(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log2' + // CHECK-FIXES: {{^}} log2f(a);{{$}} + log(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log' + // CHECK-FIXES: {{^}} logf(a);{{$}} + logb(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'logb' + // CHECK-FIXES: {{^}} logbf(a);{{$}} + lrint(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'lrint' + // CHECK-FIXES: {{^}} lrintf(a);{{$}} + lround(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'lround' + // CHECK-FIXES: {{^}} lroundf(a);{{$}} + nearbyint(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nearbyint' + // CHECK-FIXES: {{^}} nearbyintf(a);{{$}} + nextafter(a, b); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nextafter' + // CHECK-FIXES: {{^}} nextafterf(a, b);{{$}} + nexttoward(a, b); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward' + // CHECK-FIXES: {{^}} nexttowardf(a, b);{{$}} + pow(a, b); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'pow' + // CHECK-FIXES: {{^}} powf(a, b);{{$}} + remainder(a, b); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'remainder' + // CHECK-FIXES: {{^}} remainderf(a, b);{{$}} + remquo(a, b, int_ptr); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'remquo' + // CHECK-FIXES: {{^}} remquof(a, b, int_ptr);{{$}} + rint(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'rint' + // CHECK-FIXES: {{^}} rintf(a);{{$}} + round(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'round' + // CHECK-FIXES: {{^}} roundf(a);{{$}} + scalbln(a, l); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbln' + // CHECK-FIXES: {{^}} scalblnf(a, l);{{$}} + scalbn(a, i); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbn' + // CHECK-FIXES: {{^}} scalbnf(a, i);{{$}} + sin(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sin' + // CHECK-FIXES: {{^}} sinf(a);{{$}} + sinh(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sinh' + // CHECK-FIXES: {{^}} sinhf(a);{{$}} + sqrt(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sqrt' + // CHECK-FIXES: {{^}} sqrtf(a);{{$}} + tan(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'tan' + // CHECK-FIXES: {{^}} tanf(a);{{$}} + tanh(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'tanh' + // CHECK-FIXES: {{^}} tanhf(a);{{$}} + tgamma(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'tgamma' + // CHECK-FIXES: {{^}} tgammaf(a);{{$}} + trunc(a); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'trunc' + // 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' + // CHECK-FIXES: {{^}} nexttowardf(0.f, 0);{{$}} + nexttoward(0.f, 0l); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward' + // CHECK-FIXES: {{^}} nexttowardf(0.f, 0l);{{$}} + nexttoward(0.f, 0.f); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward' + // CHECK-FIXES: {{^}} nexttowardf(0.f, 0.f);{{$}} + nexttoward(0.f, 0.); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward' + // 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' + // CHECK-FIXES: {{^}} scalbnf(0.f, 0);{{$}} + scalbn(0.f, static_cast(0)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbn' + // CHECK-FIXES: {{^}} scalbnf(0.f, static_cast(0));{{$}} + + // No warnings for these. + scalbn(0., 0); + scalbn(0., static_cast(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: {{^}} scalblnf(0.f, 0);{{$}} + scalbln(0.f, 0l); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbln' + // 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' + // CHECK-FIXES: {{^}} ::sinf(0.f);{{$}} + + // Check that a space between "::" and the function name doesn't mess us up. + :: sin(0.f); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sin' + // 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); +}