Index: clang-tools-extra/trunk/clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/CMakeLists.txt +++ clang-tools-extra/trunk/clang-tidy/misc/CMakeLists.txt @@ -7,11 +7,12 @@ BoolPointerImplicitConversionCheck.cpp DefinitionsInHeadersCheck.cpp InaccurateEraseCheck.cpp + IncorrectRoundings.cpp InefficientAlgorithmCheck.cpp MacroParenthesesCheck.cpp MacroRepeatedSideEffectsCheck.cpp MiscTidyModule.cpp - MoveConstantArgumentCheck.cpp + MoveConstantArgumentCheck.cpp MoveConstructorInitCheck.cpp NewDeleteOverloadsCheck.cpp NoexceptMoveConstructorCheck.cpp Index: clang-tools-extra/trunk/clang-tidy/misc/IncorrectRoundings.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/IncorrectRoundings.h +++ clang-tools-extra/trunk/clang-tidy/misc/IncorrectRoundings.h @@ -0,0 +1,38 @@ +//===--- IncorrectRoundings.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_MISC_INCORRECTROUNDINGS_H_ +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INCORRECTROUNDINGS_H_ + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { + +/// \brief Checks the usage of patterns known to produce incorrect rounding. +/// Programmers often use +/// (int)(double_expression + 0.5) +/// to round the double expression to an integer. The problem with this +/// 1. It is unnecessarily slow. +/// 2. It is incorrect. The number 0.499999975 (smallest representable float +/// number below 0.5) rounds to 1.0. Even worse behavior for negative +/// numbers where both -0.5f and -1.4f both round to 0.0. +class IncorrectRoundings : public ClangTidyCheck { +public: + IncorrectRoundings(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace tidy +} // namespace clang + + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INCORRECTROUNDINGS_H_ Index: clang-tools-extra/trunk/clang-tidy/misc/IncorrectRoundings.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/IncorrectRoundings.cpp +++ clang-tools-extra/trunk/clang-tidy/misc/IncorrectRoundings.cpp @@ -0,0 +1,74 @@ +//===--- IncorrectRoundings.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 "IncorrectRoundings.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/Type.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Lex/Lexer.h" + +namespace clang { +namespace ast_matchers { +AST_MATCHER(FloatingLiteral, floatHalf) { + const auto &literal = Node.getValue(); + if ((&Node.getSemantics()) == &llvm::APFloat::IEEEsingle) + return literal.convertToFloat() == 0.5f; + if ((&Node.getSemantics()) == &llvm::APFloat::IEEEdouble) + return literal.convertToDouble() == 0.5; + return false; +} + +// TODO(hokein): Moving it to ASTMatchers.h +AST_MATCHER(BuiltinType, isFloatingPoint) { + return Node.isFloatingPoint(); +} +} // namespace ast_matchers +} // namespace clang + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +void IncorrectRoundings::registerMatchers(MatchFinder *MatchFinder) { + // Match a floating literal with value 0.5. + auto FloatHalf = floatLiteral(floatHalf()); + + // Match a floating point expression. + auto FloatType = expr(hasType(builtinType(isFloatingPoint()))); + + // Match a floating literal of 0.5 or a floating literal of 0.5 implicitly. + // cast to floating type. + auto FloatOrCastHalf = + anyOf(FloatHalf, implicitCastExpr(FloatType, has(FloatHalf))); + + // Match if either the LHS or RHS is a floating literal of 0.5 or a floating + // literal of 0.5 and the other is of type double or vice versa. + auto OneSideHalf = anyOf(allOf(hasLHS(FloatOrCastHalf), hasRHS(FloatType)), + allOf(hasRHS(FloatOrCastHalf), hasLHS(FloatType))); + + // Find expressions of cast to int of the sum of a floating point expression + // and 0.5. + MatchFinder->addMatcher( + implicitCastExpr( + hasImplicitDestinationType(isInteger()), + ignoringParenCasts(binaryOperator(hasOperatorName("+"), OneSideHalf))) + .bind("CastExpr"), + this); +} + +void IncorrectRoundings::check(const MatchFinder::MatchResult &Result) { + const auto *CastExpr = Result.Nodes.getStmtAs("CastExpr"); + diag(CastExpr->getLocStart(), + "casting (double + 0.5) to integer leads to incorrect rounding; " + "consider using lround (#include ) instead"); +} + +} // namespace tidy +} // namespace clang Index: clang-tools-extra/trunk/clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/MiscTidyModule.cpp +++ clang-tools-extra/trunk/clang-tidy/misc/MiscTidyModule.cpp @@ -16,6 +16,7 @@ #include "BoolPointerImplicitConversionCheck.h" #include "DefinitionsInHeadersCheck.h" #include "InaccurateEraseCheck.h" +#include "IncorrectRoundings.h" #include "InefficientAlgorithmCheck.h" #include "MacroParenthesesCheck.h" #include "MacroRepeatedSideEffectsCheck.h" @@ -54,6 +55,8 @@ "misc-definitions-in-headers"); CheckFactories.registerCheck( "misc-inaccurate-erase"); + CheckFactories.registerCheck( + "misc-incorrect-roundings"); CheckFactories.registerCheck( "misc-inefficient-algorithm"); CheckFactories.registerCheck( Index: clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst @@ -49,6 +49,7 @@ misc-bool-pointer-implicit-conversion misc-definitions-in-headers misc-inaccurate-erase + misc-incorrect-roundings misc-inefficient-algorithm misc-macro-parentheses misc-macro-repeated-side-effects Index: clang-tools-extra/trunk/docs/clang-tidy/checks/misc-incorrect-roundings.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/misc-incorrect-roundings.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/misc-incorrect-roundings.rst @@ -0,0 +1,12 @@ +misc-incorrect-roundings +======================== + +Checks the usage of patterns known to produce incorrect rounding. +Programmers often use + (int)(double_expression + 0.5) +to round the double expression to an integer. The problem with this: + +1. It is unnecessarily slow. +2. It is incorrect. The number 0.499999975 (smallest representable float + number below 0.5) rounds to 1.0. Even worse behavior for negative + numbers where both -0.5f and -1.4f both round to 0.0. Index: clang-tools-extra/trunk/test/clang-tidy/misc-incorrect-roundings.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/misc-incorrect-roundings.cpp +++ clang-tools-extra/trunk/test/clang-tidy/misc-incorrect-roundings.cpp @@ -0,0 +1,86 @@ +// RUN: %check_clang_tidy %s misc-incorrect-roundings %t + +void b(int x) {} + +void f1() { + float f; + double d; + long double ld; + int x; + + x = (d + 0.5); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5) to integer leads to incorrect rounding; consider using lround (#include ) instead [misc-incorrect-roundings] + x = (d + 0.5f); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5) + x = (f + 0.5); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5) + x = (f + 0.5f); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5) + x = (0.5 + d); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5) + x = (0.5f + d); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5) + x = (0.5 + ld); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5) + x = (0.5f + ld); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5) + x = (0.5 + f); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5) + x = (0.5f + f); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5) + x = (int)(d + 0.5); + // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5) + x = (int)(d + 0.5f); + // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5) + x = (int)(ld + 0.5); + // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5) + x = (int)(ld + 0.5f); + // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5) + x = (int)(f + 0.5); + // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5) + x = (int)(f + 0.5f); + // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5) + x = (int)(0.5 + d); + // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5) + x = (int)(0.5f + d); + // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5) + x = (int)(0.5 + ld); + // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5) + x = (int)(0.5f + ld); + // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5) + x = (int)(0.5 + f); + // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5) + x = (int)(0.5f + f); + // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5) + x = static_cast(d + 0.5); + // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5) + x = static_cast(d + 0.5f); + // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5) + x = static_cast(ld + 0.5); + // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5) + x = static_cast(ld + 0.5f); + // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5) + x = static_cast(f + 0.5); + // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5) + x = static_cast(f + 0.5f); + // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5) + x = static_cast(0.5 + d); + // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5) + x = static_cast(0.5f + d); + // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5) + x = static_cast(0.5 + ld); + // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5) + x = static_cast(0.5f + ld); + // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5) + x = static_cast(0.5 + f); + // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5) + x = static_cast(0.5f + f); + // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5) + + // Don't warn if constant is not 0.5. + x = (int)(d + 0.6); + x = (int)(0.6 + d); + + // Don't warn if binary operator is not directly beneath cast. + x = (int)(1 + (0.5 + f)); +}