-
Notifications
You must be signed in to change notification settings - Fork 12.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ClangTidy] Add new performance-type-promotion-in-math-fn check.
Summary: 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). Subscribers: mgorny, cfe-commits Differential Revision: https://reviews.llvm.org/D27284 llvm-svn: 289627
- Loading branch information
Justin Lebar
committed
Dec 14, 2016
1 parent
76a00b5
commit ecb10f4
Showing
8 changed files
with
549 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
172 changes: 172 additions & 0 deletions
172
clang-tools-extra/clang-tidy/performance/TypePromotionInMathFnCheck.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| //===--- 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" | ||
| #include "llvm/ADT/StringSet.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. | ||
| static llvm::StringSet<> Cpp11OnlyFns = { | ||
| "acosh", "asinh", "atanh", "cbrt", "copysign", "erf", | ||
| "erfc", "exp2", "expm1", "fdim", "fma", "fmax", | ||
| "fmin", "hypot", "ilogb", "lgamma", "llrint", "llround", | ||
| "log1p", "log2", "logb", "lrint", "lround", "nearbyint", | ||
| "nextafter", "nexttoward", "remainder", "remquo", "rint", "round", | ||
| "scalbln", "scalbn", "tgamma", "trunc"}; | ||
| bool StdFnRequiresCpp11 = Cpp11OnlyFns.count(OldFnName); | ||
|
|
||
| std::string NewFnName; | ||
| if (getLangOpts().CPlusPlus && | ||
| (!StdFnRequiresCpp11 || 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 |
40 changes: 40 additions & 0 deletions
40
clang-tools-extra/clang-tidy/performance/TypePromotionInMathFnCheck.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
clang-tools-extra/docs/clang-tidy/checks/performance-type-promotion-in-math-fn.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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). |
Oops, something went wrong.