Index: clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp =================================================================== --- clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp +++ clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp @@ -27,42 +27,61 @@ const auto IsFloatExpr = expr(hasType(realFloatingPointType()), unless(IsCeilFloorCall)); - // casts: + // Casts: // i = 0.5; // void f(int); f(0.5); - Finder->addMatcher(implicitCastExpr(hasImplicitDestinationType(isInteger()), - hasSourceExpression(IsFloatExpr), - unless(hasParent(castExpr())), - unless(isInTemplateInstantiation())) - .bind("cast"), - this); + Finder->addMatcher( + implicitCastExpr( + anyOf(hasImplicitDestinationType(isInteger()), + hasImplicitDestinationType(realFloatingPointType())), + hasSourceExpression(IsFloatExpr), unless(hasParent(castExpr())), + unless(isInTemplateInstantiation())) + .bind("cast"), + this); // Binary operators: // i += 0.5; Finder->addMatcher( - binaryOperator(isAssignmentOperator(), - // The `=` case generates an implicit cast which is covered - // by the previous matcher. - unless(hasOperatorName("=")), - hasLHS(hasType(isInteger())), hasRHS(IsFloatExpr), - unless(isInTemplateInstantiation())) + binaryOperator( + isAssignmentOperator(), + // The `=` case generates an implicit cast which is covered + // by the previous matcher. + unless(hasOperatorName("=")), + hasLHS(anyOf(hasType(isInteger()), hasType(realFloatingPointType()))), + hasRHS(IsFloatExpr), unless(isInTemplateInstantiation())) .bind("op"), this); } +static bool IsNarrower(const ASTContext *const Context, const QualType Lhs, + const QualType Rhs) { + assert(Lhs->isRealType()); // Either integer or floating point. + assert(Rhs->isFloatingType()); // Floating point only. + return Lhs->isIntegerType() || + (Context->getTypeSize(Rhs) > Context->getTypeSize(Lhs)); +} + void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) { - if (const auto *Op = Result.Nodes.getNodeAs("op")) { + const auto &Nodes = Result.Nodes; + if (const auto *Op = Nodes.getNodeAs("op")) { if (Op->getBeginLoc().isMacroID()) return; - diag(Op->getOperatorLoc(), "narrowing conversion from %0 to %1") - << Op->getRHS()->getType() << Op->getLHS()->getType(); - return; + const auto LhsType = Op->getLHS()->getType(); + const auto RhsType = Op->getRHS()->getType(); + if (IsNarrower(Result.Context, LhsType, RhsType)) + diag(Op->getOperatorLoc(), "narrowing conversion from %0 to %1") + << RhsType << LhsType; + } else if (const auto *Cast = Nodes.getNodeAs("cast")) { + if (Cast->getBeginLoc().isMacroID()) + return; + const auto LhsType = Cast->getType(); + const auto RhsType = Cast->getSubExpr()->getType(); + if (IsNarrower(Result.Context, LhsType, RhsType)) + diag(Cast->getExprLoc(), "narrowing conversion from %0 to %1") + << RhsType << LhsType; + } else { + llvm_unreachable("Invalid state"); } - const auto *Cast = Result.Nodes.getNodeAs("cast"); - if (Cast->getBeginLoc().isMacroID()) - return; - diag(Cast->getExprLoc(), "narrowing conversion from %0 to %1") - << Cast->getSubExpr()->getType() << Cast->getType(); } } // namespace cppcoreguidelines Index: test/clang-tidy/cppcoreguidelines-narrowing-conversions.cpp =================================================================== --- test/clang-tidy/cppcoreguidelines-narrowing-conversions.cpp +++ test/clang-tidy/cppcoreguidelines-narrowing-conversions.cpp @@ -9,12 +9,12 @@ namespace floats { struct ConvertsToFloat { - operator float() const { return 0.5; } + operator float() const { return 0.5f; } }; -float operator "" _Pa(unsigned long long); +float operator"" _float(unsigned long long); -void not_ok(double d) { +void narrow_double_to_int_not_ok(double d) { int i = 0; i = d; // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'double' to 'int' [cppcoreguidelines-narrowing-conversions] @@ -24,11 +24,11 @@ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions] i = ConvertsToFloat(); // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions] - i = 15_Pa; + i = 15_float; // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions] } -void not_ok_binary_ops(double d) { +void narrow_double_to_int_not_ok_binary_ops(double d) { int i = 0; i += 0.5; // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'double' to 'int' [cppcoreguidelines-narrowing-conversions] @@ -52,6 +52,43 @@ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'double' to 'int' [cppcoreguidelines-narrowing-conversions] } +double operator"" _double(unsigned long long); + +float narrow_double_to_float_return() { + return 0.5; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: narrowing conversion from 'double' to 'float' [cppcoreguidelines-narrowing-conversions] +} + +void narrow_double_to_float_not_ok(double d) { + float f = 0; + f = d; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'double' to 'float' [cppcoreguidelines-narrowing-conversions] + f = 0.5; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'double' to 'float' [cppcoreguidelines-narrowing-conversions] + f = 15_double; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'double' to 'float' [cppcoreguidelines-narrowing-conversions] +} + +void narrow_double_to_float_not_ok_binary_ops(double d) { + float f = 0; + f += 0.5; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'double' to 'float' [cppcoreguidelines-narrowing-conversions] + f += d; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'double' to 'float' [cppcoreguidelines-narrowing-conversions] + // We warn on the following even though it's not dangerous because there is no + // reason to use a double literal here. + // TODO(courbet): Provide an automatic fix. + f += 2.0; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'double' to 'float' [cppcoreguidelines-narrowing-conversions] + + f *= 0.5; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'double' to 'float' [cppcoreguidelines-narrowing-conversions] + f /= 0.5; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'double' to 'float' [cppcoreguidelines-narrowing-conversions] + f += (double)0.5f; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'double' to 'float' [cppcoreguidelines-narrowing-conversions] +} + void ok(double d) { int i = 0; i = 1; @@ -89,7 +126,9 @@ void template_context() { f(1, 2); + f(1, .5f); f(1, .5); + f(1, .5l); } #define DERP(i, j) (i += j) @@ -97,7 +136,9 @@ void macro_context() { int i = 0; DERP(i, 2); + DERP(i, .5f); DERP(i, .5); + DERP(i, .5l); } -} // namespace floats +} // namespace floats