[clang-tidy] Improving narrowing conversions
authorGuillaume Chatelet <gchatelet@google.com>
Mon, 26 Nov 2018 16:25:55 +0000 (16:25 +0000)
committerGuillaume Chatelet <gchatelet@google.com>
Mon, 26 Nov 2018 16:25:55 +0000 (16:25 +0000)
Summary:
Newly flagged narrowing conversions:
 - integer to narrower signed integer (this is compiler implementation defined),
 - integer - floating point narrowing conversions,
 - floating point - integer narrowing conversions,
 - constants with narrowing conversions (even in ternary operator).

Reviewers: hokein, alexfh, aaron.ballman, JonasToth

Reviewed By: aaron.ballman, JonasToth

Subscribers: lebedev.ri, courbet, nemanjai, xazax.hun, kbarton, cfe-commits

Tags: #clang-tools-extra

Differential Revision: https://reviews.llvm.org/D53488

llvm-svn: 347570

clang-tools-extra/clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp
clang-tools-extra/clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.h
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-narrowing-conversions.rst
clang-tools-extra/test/clang-tidy/cppcoreguidelines-narrowing-conversions-long-is-32bits.cpp [new file with mode: 0644]
clang-tools-extra/test/clang-tidy/cppcoreguidelines-narrowing-conversions-narrowingfloatingpoint-option.cpp [new file with mode: 0644]
clang-tools-extra/test/clang-tidy/cppcoreguidelines-narrowing-conversions-pedanticmode-option.cpp [new file with mode: 0644]
clang-tools-extra/test/clang-tidy/cppcoreguidelines-narrowing-conversions-unsigned-char.cpp [new file with mode: 0644]
clang-tools-extra/test/clang-tidy/cppcoreguidelines-narrowing-conversions.cpp

index 3a22c1e..132d84a 100644 (file)
@@ -9,7 +9,13 @@
 
 #include "NarrowingConversionsCheck.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/Type.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+
+#include <cstdint>
 
 using namespace clang::ast_matchers;
 
@@ -17,52 +23,423 @@ namespace clang {
 namespace tidy {
 namespace cppcoreguidelines {
 
-// FIXME: Check double -> float truncation. Pay attention to casts:
+NarrowingConversionsCheck::NarrowingConversionsCheck(StringRef Name,
+                                                     ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      WarnOnFloatingPointNarrowingConversion(
+          Options.get("WarnOnFloatingPointNarrowingConversion", 1)),
+      PedanticMode(Options.get("PedanticMode", 0)) {}
+
 void NarrowingConversionsCheck::registerMatchers(MatchFinder *Finder) {
   // ceil() and floor() are guaranteed to return integers, even though the type
   // is not integral.
-  const auto IsCeilFloorCall = callExpr(callee(functionDecl(
-      hasAnyName("::ceil", "::std::ceil", "::floor", "::std::floor"))));
-
-  const auto IsFloatExpr =
-      expr(hasType(realFloatingPointType()), unless(IsCeilFloorCall));
+  const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl(
+      hasAnyName("::ceil", "::std::ceil", "::floor", "::std::floor")))));
 
-  // 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(hasImplicitDestinationType(builtinType()),
+                       hasSourceExpression(hasType(builtinType())),
+                       unless(hasSourceExpression(IsCeilFloorCallExpr)),
+                       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()))
-          .bind("op"),
-      this);
+  Finder->addMatcher(binaryOperator(isAssignmentOperator(),
+                                    hasLHS(expr(hasType(builtinType()))),
+                                    hasRHS(expr(hasType(builtinType()))),
+                                    unless(hasRHS(IsCeilFloorCallExpr)),
+                                    unless(isInTemplateInstantiation()),
+                                    // The `=` case generates an implicit cast
+                                    // which is covered by the previous matcher.
+                                    unless(hasOperatorName("=")))
+                         .bind("binary_op"),
+                     this);
 }
 
-void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) {
-  if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("op")) {
-    if (Op->getBeginLoc().isMacroID())
+static const BuiltinType *getBuiltinType(const Expr &E) {
+  return E.getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>();
+}
+
+static QualType getUnqualifiedType(const Expr &E) {
+  return E.getType().getUnqualifiedType();
+}
+
+static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E) {
+  llvm::APSInt IntegerConstant;
+  if (E.isIntegerConstantExpr(IntegerConstant, Ctx))
+    return APValue(IntegerConstant);
+  APValue Constant;
+  if (Ctx.getLangOpts().CPlusPlus && E.isCXX11ConstantExpr(Ctx, &Constant))
+    return Constant;
+  return {};
+}
+
+static bool getIntegerConstantExprValue(const ASTContext &Context,
+                                        const Expr &E, llvm::APSInt &Value) {
+  APValue Constant = getConstantExprValue(Context, E);
+  if (!Constant.isInt())
+    return false;
+  Value = Constant.getInt();
+  return true;
+}
+
+static bool getFloatingConstantExprValue(const ASTContext &Context,
+                                         const Expr &E, llvm::APFloat &Value) {
+  APValue Constant = getConstantExprValue(Context, E);
+  if (!Constant.isFloat())
+    return false;
+  Value = Constant.getFloat();
+  return true;
+}
+
+namespace {
+
+struct IntegerRange {
+  bool Contains(const IntegerRange &From) const {
+    return llvm::APSInt::compareValues(Lower, From.Lower) <= 0 &&
+           llvm::APSInt::compareValues(Upper, From.Upper) >= 0;
+  }
+
+  bool Contains(const llvm::APSInt &Value) const {
+    return llvm::APSInt::compareValues(Lower, Value) <= 0 &&
+           llvm::APSInt::compareValues(Upper, Value) >= 0;
+  }
+
+  llvm::APSInt Lower;
+  llvm::APSInt Upper;
+};
+
+} // namespace
+
+static IntegerRange createFromType(const ASTContext &Context,
+                                   const BuiltinType &T) {
+  if (T.isFloatingPoint()) {
+    unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision(
+        Context.getFloatTypeSemantics(T.desugar()));
+    // Contrary to two's complement integer, floating point values are
+    // symmetric and have the same number of positive and negative values.
+    // The range of valid integers for a floating point value is:
+    // [-2^PrecisionBits, 2^PrecisionBits]
+
+    // Values are created with PrecisionBits plus two bits:
+    // - One to express the missing negative value of 2's complement
+    //   representation.
+    // - One for the sign.
+    llvm::APSInt UpperValue(PrecisionBits + 2, /*isUnsigned*/ false);
+    UpperValue.setBit(PrecisionBits);
+    llvm::APSInt LowerValue(PrecisionBits + 2, /*isUnsigned*/ false);
+    LowerValue.setBit(PrecisionBits);
+    LowerValue.setSignBit();
+    return {LowerValue, UpperValue};
+  }
+  assert(T.isInteger() && "Unexpected builtin type");
+  uint64_t TypeSize = Context.getTypeSize(&T);
+  bool IsUnsignedInteger = T.isUnsignedInteger();
+  return {llvm::APSInt::getMinValue(TypeSize, IsUnsignedInteger),
+          llvm::APSInt::getMaxValue(TypeSize, IsUnsignedInteger)};
+}
+
+static bool isWideEnoughToHold(const ASTContext &Context,
+                               const BuiltinType &FromType,
+                               const BuiltinType &ToType) {
+  IntegerRange FromIntegerRange = createFromType(Context, FromType);
+  IntegerRange ToIntegerRange = createFromType(Context, ToType);
+  return ToIntegerRange.Contains(FromIntegerRange);
+}
+
+static bool isWideEnoughToHold(const ASTContext &Context,
+                               const llvm::APSInt &IntegerConstant,
+                               const BuiltinType &ToType) {
+  IntegerRange ToIntegerRange = createFromType(Context, ToType);
+  return ToIntegerRange.Contains(IntegerConstant);
+}
+
+static llvm::SmallString<64> getValueAsString(const llvm::APSInt &Value,
+                                              uint64_t HexBits) {
+  llvm::SmallString<64> Str;
+  Value.toString(Str, 10);
+  if (HexBits > 0) {
+    Str.append(" (0x");
+    llvm::SmallString<32> HexValue;
+    Value.toStringUnsigned(HexValue, 16);
+    for (size_t I = HexValue.size(); I < (HexBits / 4); ++I)
+      Str.append("0");
+    Str.append(HexValue);
+    Str.append(")");
+  }
+  return Str;
+}
+
+void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc,
+                                               const Expr &Lhs,
+                                               const Expr &Rhs) {
+  diag(SourceLoc, "narrowing conversion from %0 to %1")
+      << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
+}
+
+void NarrowingConversionsCheck::diagNarrowTypeToSignedInt(
+    SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs) {
+  diag(SourceLoc, "narrowing conversion from %0 to signed type %1 is "
+                  "implementation-defined")
+      << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
+}
+
+void NarrowingConversionsCheck::diagNarrowIntegerConstant(
+    SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,
+    const llvm::APSInt &Value) {
+  diag(SourceLoc,
+       "narrowing conversion from constant value %0 of type %1 to %2")
+      << getValueAsString(Value, /*NoHex*/ 0) << getUnqualifiedType(Rhs)
+      << getUnqualifiedType(Lhs);
+}
+
+void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt(
+    SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,
+    const llvm::APSInt &Value, const uint64_t HexBits) {
+  diag(SourceLoc, "narrowing conversion from constant value %0 of type %1 "
+                  "to signed type %2 is implementation-defined")
+      << getValueAsString(Value, HexBits) << getUnqualifiedType(Rhs)
+      << getUnqualifiedType(Lhs);
+}
+
+void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc,
+                                                   const Expr &Lhs,
+                                                   const Expr &Rhs) {
+  diag(SourceLoc, "narrowing conversion from constant %0 to %1")
+      << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
+}
+
+void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc,
+                                                 const Expr &Lhs,
+                                                 const Expr &Rhs) {
+  diag(SourceLoc, "constant value should be of type of type %0 instead of %1")
+      << getUnqualifiedType(Lhs) << getUnqualifiedType(Rhs);
+}
+
+void NarrowingConversionsCheck::diagNarrowTypeOrConstant(
+    const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
+    const Expr &Rhs) {
+  APValue Constant = getConstantExprValue(Context, Rhs);
+  if (Constant.isInt())
+    return diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, Constant.getInt());
+  if (Constant.isFloat())
+    return diagNarrowConstant(SourceLoc, Lhs, Rhs);
+  return diagNarrowType(SourceLoc, Lhs, Rhs);
+}
+
+void NarrowingConversionsCheck::handleIntegralCast(const ASTContext &Context,
+                                                   SourceLocation SourceLoc,
+                                                   const Expr &Lhs,
+                                                   const Expr &Rhs) {
+  const BuiltinType *ToType = getBuiltinType(Lhs);
+  // From [conv.integral]p7.3.8:
+  // Conversions to unsigned integer is well defined so no warning is issued.
+  // "The resulting value is the smallest unsigned value equal to the source
+  // value modulo 2^n where n is the number of bits used to represent the
+  // destination type."
+  if (ToType->isUnsignedInteger())
+    return;
+  const BuiltinType *FromType = getBuiltinType(Rhs);
+  llvm::APSInt IntegerConstant;
+  if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {
+    if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))
+      diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs, IntegerConstant,
+                                           Context.getTypeSize(FromType));
+    return;
+  }
+  if (!isWideEnoughToHold(Context, *FromType, *ToType))
+    diagNarrowTypeToSignedInt(SourceLoc, Lhs, Rhs);
+}
+
+void NarrowingConversionsCheck::handleIntegralToBoolean(
+    const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
+    const Expr &Rhs) {
+  // Conversion from Integral to Bool value is well defined.
+
+  // We keep this function (even if it is empty) to make sure that
+  // handleImplicitCast and handleBinaryOperator are symmetric in their behavior
+  // and handle the same cases.
+}
+
+void NarrowingConversionsCheck::handleIntegralToFloating(
+    const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
+    const Expr &Rhs) {
+  const BuiltinType *ToType = getBuiltinType(Lhs);
+  llvm::APSInt IntegerConstant;
+  if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {
+    if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))
+      diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant);
+    return;
+  }
+  const BuiltinType *FromType = getBuiltinType(Rhs);
+  if (!isWideEnoughToHold(Context, *FromType, *ToType))
+    diagNarrowType(SourceLoc, Lhs, Rhs);
+}
+
+void NarrowingConversionsCheck::handleFloatingToIntegral(
+    const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
+    const Expr &Rhs) {
+  llvm::APFloat FloatConstant(0.0);
+
+  // We always warn when Rhs is non-constexpr.
+  if (!getFloatingConstantExprValue(Context, Rhs, FloatConstant))
+    return diagNarrowType(SourceLoc, Lhs, Rhs);
+
+  QualType DestType = Lhs.getType();
+  unsigned DestWidth = Context.getIntWidth(DestType);
+  bool DestSigned = DestType->isSignedIntegerOrEnumerationType();
+  llvm::APSInt Result = llvm::APSInt(DestWidth, !DestSigned);
+  bool IsExact = false;
+  bool Overflows = FloatConstant.convertToInteger(
+                       Result, llvm::APFloat::rmTowardZero, &IsExact) &
+                   llvm::APFloat::opInvalidOp;
+  // We warn iff the constant floating point value is not exactly representable.
+  if (Overflows || !IsExact)
+    return diagNarrowConstant(SourceLoc, Lhs, Rhs);
+
+  if (PedanticMode)
+    return diagConstantCast(SourceLoc, Lhs, Rhs);
+}
+
+void NarrowingConversionsCheck::handleFloatingToBoolean(
+    const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
+    const Expr &Rhs) {
+  return diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs);
+}
+
+void NarrowingConversionsCheck::handleBooleanToSignedIntegral(
+    const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
+    const Expr &Rhs) {
+  // Conversion from Bool to SignedIntegral value is well defined.
+
+  // We keep this function (even if it is empty) to make sure that
+  // handleImplicitCast and handleBinaryOperator are symmetric in their behavior
+  // and handle the same cases.
+}
+
+void NarrowingConversionsCheck::handleFloatingCast(const ASTContext &Context,
+                                                   SourceLocation SourceLoc,
+                                                   const Expr &Lhs,
+                                                   const Expr &Rhs) {
+  if (WarnOnFloatingPointNarrowingConversion) {
+    const BuiltinType *ToType = getBuiltinType(Lhs);
+    APValue Constant = getConstantExprValue(Context, Rhs);
+    if (Constant.isFloat()) {
+      // From [dcl.init.list]p7.2:
+      // Floating point constant narrowing only takes place when the value is
+      // not within destination range. We convert the value to the destination
+      // type and check if the resulting value is infinity.
+      llvm::APFloat Tmp = Constant.getFloat();
+      bool UnusedLosesInfo;
+      Tmp.convert(Context.getFloatTypeSemantics(ToType->desugar()),
+                  llvm::APFloatBase::rmNearestTiesToEven, &UnusedLosesInfo);
+      if (Tmp.isInfinity())
+        diagNarrowConstant(SourceLoc, Lhs, Rhs);
       return;
-    diag(Op->getOperatorLoc(), "narrowing conversion from %0 to %1")
-        << Op->getRHS()->getType() << Op->getLHS()->getType();
+    }
+    const BuiltinType *FromType = getBuiltinType(Rhs);
+    if (ToType->getKind() < FromType->getKind())
+      diagNarrowType(SourceLoc, Lhs, Rhs);
+  }
+}
+
+void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,
+                                                     SourceLocation SourceLoc,
+                                                     const Expr &Lhs,
+                                                     const Expr &Rhs) {
+  assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() &&
+         "Dependent types must be check before calling this function");
+  const BuiltinType *LhsType = getBuiltinType(Lhs);
+  const BuiltinType *RhsType = getBuiltinType(Rhs);
+  if (RhsType == nullptr || LhsType == nullptr)
     return;
+  if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger())
+    return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
+  if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool)
+    return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
+  if (RhsType->isInteger() && LhsType->isFloatingPoint())
+    return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
+  if (RhsType->isInteger() && LhsType->isInteger())
+    return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
+  if (RhsType->isFloatingPoint() && LhsType->getKind() == BuiltinType::Bool)
+    return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
+  if (RhsType->isFloatingPoint() && LhsType->isInteger())
+    return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
+  if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint())
+    return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
+}
+
+bool NarrowingConversionsCheck::handleConditionalOperator(
+    const ASTContext &Context, const Expr &Lhs, const Expr &Rhs) {
+  if (const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) {
+    // We have an expression like so: `output = cond ? lhs : rhs`
+    // From the point of view of narrowing conversion we treat it as two
+    // expressions `output = lhs` and `output = rhs`.
+    handleBinaryOperator(Context, CO->getLHS()->getExprLoc(), Lhs,
+                         *CO->getLHS());
+    handleBinaryOperator(Context, CO->getRHS()->getExprLoc(), Lhs,
+                         *CO->getRHS());
+    return true;
   }
-  const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast");
-  if (Cast->getBeginLoc().isMacroID())
+  return false;
+}
+
+void NarrowingConversionsCheck::handleImplicitCast(
+    const ASTContext &Context, const ImplicitCastExpr &Cast) {
+  if (Cast.getExprLoc().isMacroID())
     return;
-  diag(Cast->getExprLoc(), "narrowing conversion from %0 to %1")
-      << Cast->getSubExpr()->getType() << Cast->getType();
+  const Expr &Lhs = Cast;
+  const Expr &Rhs = *Cast.getSubExpr();
+  if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
+    return;
+  if (handleConditionalOperator(Context, Lhs, Rhs))
+    return;
+  SourceLocation SourceLoc = Lhs.getExprLoc();
+  switch (Cast.getCastKind()) {
+  case CK_BooleanToSignedIntegral:
+    return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
+  case CK_IntegralToBoolean:
+    return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
+  case CK_IntegralToFloating:
+    return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
+  case CK_IntegralCast:
+    return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
+  case CK_FloatingToBoolean:
+    return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
+  case CK_FloatingToIntegral:
+    return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
+  case CK_FloatingCast:
+    return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
+  default:
+    break;
+  }
+}
+
+void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,
+                                                     const BinaryOperator &Op) {
+  if (Op.getBeginLoc().isMacroID())
+    return;
+  const Expr &Lhs = *Op.getLHS();
+  const Expr &Rhs = *Op.getRHS();
+  if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
+    return;
+  if (handleConditionalOperator(Context, Lhs, Rhs))
+    return;
+  handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs);
+}
+
+void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("binary_op"))
+    return handleBinaryOperator(*Result.Context, *Op);
+  if (const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast"))
+    return handleImplicitCast(*Result.Context, *Cast);
+  llvm_unreachable("must be binary operator or cast expression");
 }
 
 } // namespace cppcoreguidelines
index 35f58bc..3e72248 100644 (file)
@@ -24,10 +24,76 @@ namespace cppcoreguidelines {
 /// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-narrowing-conversions.html
 class NarrowingConversionsCheck : public ClangTidyCheck {
 public:
-  NarrowingConversionsCheck(StringRef Name, ClangTidyContext *Context)
-      : ClangTidyCheck(Name, Context) {}
+  NarrowingConversionsCheck(StringRef Name, ClangTidyContext *Context);
+
   void registerMatchers(ast_matchers::MatchFinder *Finder) override;
   void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void diagNarrowType(SourceLocation SourceLoc, const Expr &Lhs,
+                      const Expr &Rhs);
+
+  void diagNarrowTypeToSignedInt(SourceLocation SourceLoc, const Expr &Lhs,
+                                 const Expr &Rhs);
+
+  void diagNarrowIntegerConstant(SourceLocation SourceLoc, const Expr &Lhs,
+                                 const Expr &Rhs, const llvm::APSInt &Value);
+
+  void diagNarrowIntegerConstantToSignedInt(SourceLocation SourceLoc,
+                                            const Expr &Lhs, const Expr &Rhs,
+                                            const llvm::APSInt &Value,
+                                            const uint64_t HexBits);
+
+  void diagNarrowConstant(SourceLocation SourceLoc, const Expr &Lhs,
+                          const Expr &Rhs);
+
+  void diagConstantCast(SourceLocation SourceLoc, const Expr &Lhs,
+                        const Expr &Rhs);
+
+  void diagNarrowTypeOrConstant(const ASTContext &Context,
+                                SourceLocation SourceLoc, const Expr &Lhs,
+                                const Expr &Rhs);
+
+  void handleIntegralCast(const ASTContext &Context, SourceLocation SourceLoc,
+                          const Expr &Lhs, const Expr &Rhs);
+
+  void handleIntegralToBoolean(const ASTContext &Context,
+                               SourceLocation SourceLoc, const Expr &Lhs,
+                               const Expr &Rhs);
+
+  void handleIntegralToFloating(const ASTContext &Context,
+                                SourceLocation SourceLoc, const Expr &Lhs,
+                                const Expr &Rhs);
+
+  void handleFloatingToIntegral(const ASTContext &Context,
+                                SourceLocation SourceLoc, const Expr &Lhs,
+                                const Expr &Rhs);
+
+  void handleFloatingToBoolean(const ASTContext &Context,
+                               SourceLocation SourceLoc, const Expr &Lhs,
+                               const Expr &Rhs);
+
+  void handleBooleanToSignedIntegral(const ASTContext &Context,
+                                     SourceLocation SourceLoc, const Expr &Lhs,
+                                     const Expr &Rhs);
+
+  void handleFloatingCast(const ASTContext &Context, SourceLocation SourceLoc,
+                          const Expr &Lhs, const Expr &Rhs);
+
+  void handleBinaryOperator(const ASTContext &Context, SourceLocation SourceLoc,
+                            const Expr &Lhs, const Expr &Rhs);
+
+  bool handleConditionalOperator(const ASTContext &Context, const Expr &Lhs,
+                                 const Expr &Rhs);
+
+  void handleImplicitCast(const ASTContext &Context,
+                          const ImplicitCastExpr &Cast);
+
+  void handleBinaryOperator(const ASTContext &Context,
+                            const BinaryOperator &Op);
+
+  const bool WarnOnFloatingPointNarrowingConversion;
+  const bool PedanticMode;
 };
 
 } // namespace cppcoreguidelines
index 2773e23..c7229df 100644 (file)
@@ -217,6 +217,14 @@ Improvements to clang-tidy
   <clang-tidy/checks/readability-redundant-smartptr-get>` check does not warn
   about calls inside macros anymore by default.
 
+- The :doc:`cppcoreguidelines-narrowing-conversions
+  <clang-tidy/checks/cppcoreguidelines-narrowing-conversions>` check now
+  detects more narrowing conversions:
+  - integer to narrower signed integer (this is compiler implementation defined),
+  - integer - floating point narrowing conversions,
+  - floating point - integer narrowing conversions,
+  - constants with narrowing conversions (even in ternary operator).
+
 Improvements to include-fixer
 -----------------------------
 
index b1e82ca..8c9bce1 100644 (file)
@@ -10,13 +10,55 @@ following: ``void MyClass::f(double d) { int_member_ += d; }``.
 This rule is part of the "Expressions and statements" profile of the C++ Core
 Guidelines, corresponding to rule ES.46. See
 
-https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Res-narrowing.
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es46-avoid-lossy-narrowing-truncating-arithmetic-conversions.
 
-We enforce only part of the guideline, more specifically, we flag:
- - All floating-point to integer conversions that are not marked by an explicit
-   cast (c-style or ``static_cast``). For example: ``int i = 0; i += 0.1;``,
+We enforce only part of the guideline, more specifically, we flag narrowing conversions from:
+ - an integer to a narrower integer (e.g. ``char`` to ``unsigned char``),
+ - an integer to a narrower floating-point (e.g. ``uint64_t`` to ``float``),
+ - a floating-point to an integer (e.g. ``double`` to ``int``),
+ - a floating-point to a narrower floating-point (e.g. ``double`` to ``float``)
+   if WarnOnFloatingPointNarrowingConversion Option is set.
+
+This check will flag:
+ - All narrowing conversions that are not marked by an explicit cast (c-style or
+   ``static_cast``). For example: ``int i = 0; i += 0.1;``,
    ``void f(int); f(0.1);``,
- - All applications of binary operators where the left-hand-side is an integer
-   and the right-hand-size is a floating-point. For example:
-   ``int i; i+= 0.1;``.
+ - All applications of binary operators with a narrowing conversions.
+   For example: ``int i; i+= 0.1;``.
+
+
+ Options
+ -------
+
+ .. option:: WarnOnFloatingPointNarrowingConversion
+
+    When non-zero, the check will warn on narrowing floating point conversion
+    (e.g. ``double`` to ``float``). `1` by default.
+
+  .. option:: PedanticMode
+
+    When non-zero, the check will warn on assigning a floating point constant
+    to an integer value even if the floating point value is exactly
+    representable in the destination type (e.g. ``int i = 1.0;``).
+    `0` by default.
+
+FAQ
+---
+
+ - What does "narrowing conversion from 'int' to 'float'" mean?
+
+An IEEE754 Floating Point number can represent all integer values in the range
+[-2^PrecisionBits, 2^PrecisionBits] where PrecisionBits is the number of bits in
+the mantissa.
+
+For ``float`` this would be [-2^23, 2^23], where ``int`` can represent values in
+the range [-2^31, 2^31-1].
+
+ - What does "implementation-defined" mean?
 
+You may have encountered messages like "narrowing conversion from 'unsigned int'
+to signed type 'int' is implementation-defined".
+The C/C++ standard does not mandate two’s complement for signed integers, and so
+the compiler is free to define what the semantics are for converting an unsigned
+integer to signed integer. Clang's implementation uses the two’s complement
+format.
diff --git a/clang-tools-extra/test/clang-tidy/cppcoreguidelines-narrowing-conversions-long-is-32bits.cpp b/clang-tools-extra/test/clang-tidy/cppcoreguidelines-narrowing-conversions-long-is-32bits.cpp
new file mode 100644 (file)
index 0000000..dcf1848
--- /dev/null
@@ -0,0 +1,23 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-narrowing-conversions %t \
+// RUN: -- -- -target x86_64-unknown-linux -m32
+
+static_assert(sizeof(int) * 8 == 32, "int is 32-bits");
+static_assert(sizeof(long) * 8 == 32, "long is 32-bits");
+static_assert(sizeof(long long) * 8 == 64, "long long is 64-bits");
+
+void narrow_integer_to_signed_integer_is_not_ok() {
+  int i;        // i.e. int32_t
+  long l;       // i.e. int32_t
+  long long ll; // i.e. int64_t
+
+  unsigned int ui;        // i.e. uint32_t
+  unsigned long ul;       // i.e. uint32_t
+  unsigned long long ull; // i.e. uint64_t
+
+  i = l;  // int and long are the same type.
+  i = ll; // int64_t does not fit in an int32_t
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'long long' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  ll = ul;  // uint32_t fits into int64_t
+  ll = ull; // uint64_t does not fit in an int64_t
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'unsigned long long' to signed type 'long long' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+}
diff --git a/clang-tools-extra/test/clang-tidy/cppcoreguidelines-narrowing-conversions-narrowingfloatingpoint-option.cpp b/clang-tools-extra/test/clang-tidy/cppcoreguidelines-narrowing-conversions-narrowingfloatingpoint-option.cpp
new file mode 100644 (file)
index 0000000..14840df
--- /dev/null
@@ -0,0 +1,57 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-narrowing-conversions %t \
+// RUN: -- -- -target x86_64-unknown-linux -fsigned-char
+
+namespace floats {
+
+void narrow_constant_floating_point_to_int_not_ok(double d) {
+  int i = 0;
+  i += 0.5;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'double' to 'int' [cppcoreguidelines-narrowing-conversions]
+  i += 0.5f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
+  i *= 0.5f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
+  i /= 0.5f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
+  i += (double)0.5f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'double' to 'int' [cppcoreguidelines-narrowing-conversions]
+  i += 2.0;
+  i += 2.0f;
+}
+
+double operator"" _double(unsigned long long);
+
+float narrow_double_to_float_return() {
+  return 0.5;
+}
+
+void narrow_double_to_float_not_ok(double d) {
+  float f;
+  f = d;
+  // 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]
+  f += d;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'double' to 'float' [cppcoreguidelines-narrowing-conversions]
+  f = narrow_double_to_float_return();
+}
+
+void narrow_fp_constants() {
+  float f;
+  f = 0.5; // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing.
+
+  f = __builtin_huge_valf();  // max float is not narrowing.
+  f = -__builtin_huge_valf(); // -max float is not narrowing.
+  f = __builtin_inff();       // float infinity is not narrowing.
+  f = __builtin_nanf("0");    // float NaN is not narrowing.
+
+  f = __builtin_huge_val(); // max double is not within-range of float.
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from constant 'double' to 'float' [cppcoreguidelines-narrowing-conversions]
+  f = -__builtin_huge_val(); // -max double is not within-range of float.
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from constant 'double' to 'float' [cppcoreguidelines-narrowing-conversions]
+  f = __builtin_inf(); // double infinity is not within-range of float.
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from constant 'double' to 'float' [cppcoreguidelines-narrowing-conversions]
+  f = __builtin_nan("0"); // double NaN is not narrowing.
+}
+
+} // namespace floats
diff --git a/clang-tools-extra/test/clang-tidy/cppcoreguidelines-narrowing-conversions-pedanticmode-option.cpp b/clang-tools-extra/test/clang-tidy/cppcoreguidelines-narrowing-conversions-pedanticmode-option.cpp
new file mode 100644 (file)
index 0000000..f28985d
--- /dev/null
@@ -0,0 +1,23 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-narrowing-conversions %t \
+// RUN: -config="{CheckOptions: [ \
+// RUN:   {key: "cppcoreguidelines-narrowing-conversions.PedanticMode", value: 1} \
+// RUN: ]}" \
+// RUN: -- -target x86_64-unknown-linux -fsigned-char
+
+namespace floats {
+
+void triggers_wrong_constant_type_warning(double d) {
+  int i = 0.0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: constant value should be of type of type 'int' instead of 'double' [cppcoreguidelines-narrowing-conversions]
+  i += 2.0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: constant value should be of type of type 'int' instead of 'double' [cppcoreguidelines-narrowing-conversions]
+  i += 2.0f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: constant value should be of type of type 'int' instead of 'float' [cppcoreguidelines-narrowing-conversions]
+}
+
+void triggers_narrowing_warning_when_overflowing() {
+  unsigned short us = 65537.0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: narrowing conversion from constant 'double' to 'unsigned short' [cppcoreguidelines-narrowing-conversions]
+}
+
+} // namespace floats
diff --git a/clang-tools-extra/test/clang-tidy/cppcoreguidelines-narrowing-conversions-unsigned-char.cpp b/clang-tools-extra/test/clang-tidy/cppcoreguidelines-narrowing-conversions-unsigned-char.cpp
new file mode 100644 (file)
index 0000000..6bd437f
--- /dev/null
@@ -0,0 +1,83 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-narrowing-conversions %t \
+// RUN: -- -- -target x86_64-unknown-linux -funsigned-char
+
+void narrow_integer_to_unsigned_integer_is_ok() {
+  signed char sc;
+  short s;
+  int i;
+  long l;
+  long long ll;
+
+  char c;
+  unsigned short us;
+  unsigned int ui;
+  unsigned long ul;
+  unsigned long long ull;
+
+  ui = sc;
+  c = s;
+  c = i;
+  c = l;
+  c = ll;
+
+  c = c;
+  c = us;
+  c = ui;
+  c = ul;
+  c = ull;
+}
+
+void narrow_integer_to_signed_integer_is_not_ok() {
+  signed char sc;
+  short s;
+  int i;
+  long l;
+  long long ll;
+
+  char c;
+  unsigned short us;
+  unsigned int ui;
+  unsigned long ul;
+  unsigned long long ull;
+
+  sc = sc;
+  sc = s;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'short' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  sc = i;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'int' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  sc = l;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'long' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  sc = ll;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'long long' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+
+  sc = c;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'char' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  sc = us;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'unsigned short' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  sc = ui;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'unsigned int' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  sc = ul;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'unsigned long' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  sc = ull;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'unsigned long long' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+}
+
+void narrow_constant_to_unsigned_integer_is_ok() {
+  char c1 = -128; // unsigned dst type is well defined.
+  char c2 = 127;  // unsigned dst type is well defined.
+  char c3 = -129; // unsigned dst type is well defined.
+  char c4 = 128;  // unsigned dst type is well defined.
+  unsigned char uc1 = 0;
+  unsigned char uc2 = 255;
+  unsigned char uc3 = -1;  // unsigned dst type is well defined.
+  unsigned char uc4 = 256; // unsigned dst type is well defined.
+  signed char sc = 128;
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: narrowing conversion from constant value 128 (0x00000080) of type 'int' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+}
+
+void narrow_conditional_operator_contant_to_unsigned_is_ok(bool b) {
+  // conversion to unsigned char type is well defined.
+  char c1 = b ? 1 : 0;
+  char c2 = b ? 1 : 256;
+  char c3 = b ? -1 : 0;
+}
index ecffba6..cc817a0 100644 (file)
@@ -1,4 +1,8 @@
-// RUN: %check_clang_tidy %s cppcoreguidelines-narrowing-conversions %t
+// RUN: %check_clang_tidy %s cppcoreguidelines-narrowing-conversions %t \
+// RUN: -config="{CheckOptions: [ \
+// RUN:   {key: "cppcoreguidelines-narrowing-conversions.WarnOnFloatingPointNarrowingConversion", value: 0}, \
+// RUN: ]}" \
+// RUN: -- -target x86_64-unknown-linux -fsigned-char
 
 float ceil(float);
 namespace std {
@@ -9,47 +13,282 @@ long double floor(long double);
 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_fp_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]
   i = 0.5f;
-  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
   i = static_cast<float>(d);
   // 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) {
-  int i = 0;
+  i += d;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'double' to 'int' [cppcoreguidelines-narrowing-conversions]
   i += 0.5;
-  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'double' to 'int' [cppcoreguidelines-narrowing-conversions]
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'double' to 'int' [cppcoreguidelines-narrowing-conversions]
   i += 0.5f;
-  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
-  i += d;
-  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'double' to 'int' [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.
-  i += 2.0;
-  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'double' to 'int' [cppcoreguidelines-narrowing-conversions]
-  i += 2.0f;
-  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
-
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
   i *= 0.5f;
-  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
   i /= 0.5f;
-  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions]
   i += (double)0.5f;
-  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'double' to 'int' [cppcoreguidelines-narrowing-conversions]
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'double' to 'int' [cppcoreguidelines-narrowing-conversions]
+  i += 2.0;
+  i += 2.0f;
+}
+
+double operator"" _double(unsigned long long);
+
+float narrow_double_to_float_return() {
+  return 0.5; // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing.
+}
+
+void narrow_double_to_float_ok(double d) {
+  float f;
+  f = d;
+  f = 15_double;
+}
+
+void narrow_fp_constants() {
+  float f;
+  f = 0.5; // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing.
+
+  f = __builtin_huge_valf();  // max float is not narrowing.
+  f = -__builtin_huge_valf(); // -max float is not narrowing.
+  f = __builtin_inff();       // float infinity is not narrowing.
+  f = __builtin_nanf("0");    // float NaN is not narrowing.
+
+  f = __builtin_huge_val();  // max double is not within-range of float.
+  f = -__builtin_huge_val(); // -max double is not within-range of float.
+  f = __builtin_inf();       // double infinity is not within-range of float.
+  f = __builtin_nan("0");    // double NaN is not narrowing.
+}
+
+void narrow_double_to_float_not_ok_binary_ops(double d) {
+  float f;
+  f += 0.5;          // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing.
+  f += 2.0;          // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing.
+  f *= 0.5;          // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing.
+  f /= 0.5;          // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing.
+  f += (double)0.5f; // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing.
+  f += d;            // We do not warn about floating point narrowing by default.
+}
+
+void narrow_fp_constant_to_bool_not_ok() {
+  bool b1 = 1.0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: narrowing conversion from constant 'double' to 'bool' [cppcoreguidelines-narrowing-conversions]
+  bool b2 = 1.0f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: narrowing conversion from constant 'float' to 'bool' [cppcoreguidelines-narrowing-conversions]
+}
+
+void narrow_integer_to_floating() {
+  {
+    long long ll; // 64 bits
+    float f = ll; // doesn't fit in 24 bits
+    // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: narrowing conversion from 'long long' to 'float' [cppcoreguidelines-narrowing-conversions]
+    double d = ll; // doesn't fit in 53 bits.
+    // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: narrowing conversion from 'long long' to 'double' [cppcoreguidelines-narrowing-conversions]
+  }
+  {
+    int i;       // 32 bits
+    float f = i; // doesn't fit in 24 bits
+    // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: narrowing conversion from 'int' to 'float' [cppcoreguidelines-narrowing-conversions]
+    double d = i; // fits in 53 bits.
+  }
+  {
+    short n1, n2;
+    float f = n1 + n2; // 'n1 + n2' is of type 'int' because of integer rules
+    // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: narrowing conversion from 'int' to 'float' [cppcoreguidelines-narrowing-conversions]
+  }
+  {
+    short s;      // 16 bits
+    float f = s;  // fits in 24 bits
+    double d = s; // fits in 53 bits.
+  }
+}
+
+void narrow_integer_to_unsigned_integer_is_ok() {
+  char c;
+  short s;
+  int i;
+  long l;
+  long long ll;
+
+  unsigned char uc;
+  unsigned short us;
+  unsigned int ui;
+  unsigned long ul;
+  unsigned long long ull;
+
+  ui = c;
+  uc = s;
+  uc = i;
+  uc = l;
+  uc = ll;
+
+  uc = uc;
+  uc = us;
+  uc = ui;
+  uc = ul;
+  uc = ull;
+}
+
+void narrow_integer_to_signed_integer_is_not_ok() {
+  char c;
+  short s;
+  int i;
+  long l;
+  long long ll;
+
+  unsigned char uc;
+  unsigned short us;
+  unsigned int ui;
+  unsigned long ul;
+  unsigned long long ull;
+
+  c = c;
+  c = s;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'short' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  c = i;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  c = l;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'long' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  c = ll;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'long long' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+
+  c = uc;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned char' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  c = us;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned short' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  c = ui;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  c = ul;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned long' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  c = ull;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned long long' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+
+  i = c;
+  i = s;
+  i = i;
+  i = l;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'long' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  i = ll;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'long long' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+
+  i = uc;
+  i = us;
+  i = ui;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned int' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  i = ul;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned long' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  i = ull;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned long long' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+
+  ll = c;
+  ll = s;
+  ll = i;
+  ll = l;
+  ll = ll;
+
+  ll = uc;
+  ll = us;
+  ll = ui;
+  ll = ul;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'unsigned long' to signed type 'long long' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  ll = ull;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'unsigned long long' to signed type 'long long' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+}
+
+void narrow_constant_to_unsigned_integer_is_ok() {
+  unsigned char uc1 = 0;
+  unsigned char uc2 = 255;
+  unsigned char uc3 = -1;  // unsigned dst type is well defined.
+  unsigned char uc4 = 256; // unsigned dst type is well defined.
+  unsigned short us1 = 0;
+  unsigned short us2 = 65535;
+  unsigned short us3 = -1;    // unsigned dst type is well defined.
+  unsigned short us4 = 65536; // unsigned dst type is well defined.
+}
+
+void narrow_constant_to_signed_integer_is_not_ok() {
+  char c1 = -128;
+  char c2 = 127;
+  char c3 = -129;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: narrowing conversion from constant value -129 (0xFFFFFF7F) of type 'int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  char c4 = 128;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: narrowing conversion from constant value 128 (0x00000080) of type 'int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+
+  short s1 = -32768;
+  short s2 = 32767;
+  short s3 = -32769;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: narrowing conversion from constant value -32769 (0xFFFF7FFF) of type 'int' to signed type 'short' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  short s4 = 32768;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: narrowing conversion from constant value 32768 (0x00008000) of type 'int' to signed type 'short' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+}
+
+void narrow_conditional_operator_contant_to_unsigned_is_ok(bool b) {
+  // conversion to unsigned dst type is well defined.
+  unsigned char c1 = b ? 1 : 0;
+  unsigned char c2 = b ? 1 : 256;
+  unsigned char c3 = b ? -1 : 0;
+}
+
+void narrow_conditional_operator_contant_to_signed_is_not_ok(bool b) {
+  char uc1 = b ? 1 : 0;
+  char uc2 = b ? 1 : 128;
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: narrowing conversion from constant value 128 (0x00000080) of type 'int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  char uc3 = b ? -129 : 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: narrowing conversion from constant value -129 (0xFFFFFF7F) of type 'int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  unsigned long long ysize;
+  long long mirror = b ? -1 : ysize - 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: narrowing conversion from constant value 18446744073709551615 (0xFFFFFFFFFFFFFFFF) of type 'unsigned long long' to signed type 'long long' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+  // CHECK-MESSAGES: :[[@LINE-2]]:37: warning: narrowing conversion from 'unsigned long long' to signed type 'long long' is implementation-defined [cppcoreguidelines-narrowing-conversions]
+}
+
+void narrow_constant_to_floating_point() {
+  float f_ok = 1ULL << 24;              // fits in 24 bits mantissa.
+  float f_not_ok = (1ULL << 24) + 1ULL; // doesn't fit in 24 bits mantissa.
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: narrowing conversion from constant value 16777217 of type 'unsigned long long' to 'float' [cppcoreguidelines-narrowing-conversions]
+  double d_ok = 1ULL << 53;              // fits in 53 bits mantissa.
+  double d_not_ok = (1ULL << 53) + 1ULL; // doesn't fit in 53 bits mantissa.
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: narrowing conversion from constant value 9007199254740993 of type 'unsigned long long' to 'double' [cppcoreguidelines-narrowing-conversions]
+}
+
+void casting_integer_to_bool_is_ok() {
+  int i;
+  while (i) {
+  }
+  for (; i;) {
+  }
+  if (i) {
+  }
+}
+
+void casting_float_to_bool_is_not_ok() {
+  float f;
+  while (f) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: narrowing conversion from 'float' to 'bool' [cppcoreguidelines-narrowing-conversions]
+  }
+  for (; f;) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: narrowing conversion from 'float' to 'bool' [cppcoreguidelines-narrowing-conversions]
+  }
+  if (f) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'float' to 'bool' [cppcoreguidelines-narrowing-conversions]
+  }
+}
+
+void legitimate_comparison_do_not_warn(unsigned long long size) {
+  for (int i = 0; i < size; ++i) {
+  }
 }
 
 void ok(double d) {
@@ -89,7 +328,9 @@ void f(T1 one, T2 two) {
 
 void template_context() {
   f(1, 2);
+  f(1, .5f);
   f(1, .5);
+  f(1, .5l);
 }
 
 #define DERP(i, j) (i += j)
@@ -97,7 +338,9 @@ void template_context() {
 void macro_context() {
   int i = 0;
   DERP(i, 2);
+  DERP(i, .5f);
   DERP(i, .5);
+  DERP(i, .5l);
 }
 
-}  // namespace floats
+} // namespace floats