brintos

brintos / llvm-project-archived public Read only

0
0
Text · 26.3 KiB · 501a82d Raw
636 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "NarrowingConversionsCheck.h"10#include "../utils/OptionsUtils.h"11#include "clang/AST/ASTContext.h"12#include "clang/AST/Expr.h"13#include "clang/AST/Type.h"14#include "clang/ASTMatchers/ASTMatchFinder.h"15#include "clang/ASTMatchers/ASTMatchers.h"16#include "llvm/ADT/APSInt.h"17#include "llvm/ADT/STLExtras.h"18#include "llvm/ADT/SmallString.h"19 20#include <cstdint>21 22using namespace clang::ast_matchers;23 24namespace clang::tidy::bugprone {25 26namespace {27 28AST_MATCHER_P(QualType, hasAnyType, std::vector<StringRef>, Names) {29  if (Names.empty())30    return false;31 32  const std::string Name = Node.getLocalUnqualifiedType().getAsString();33  return llvm::is_contained(Names, Name);34}35 36AST_MATCHER(FieldDecl, hasIntBitwidth) {37  assert(Node.isBitField());38  const ASTContext &Ctx = Node.getASTContext();39  const unsigned IntBitWidth = Ctx.getIntWidth(Ctx.IntTy);40  const unsigned CurrentBitWidth = Node.getBitWidthValue();41  return IntBitWidth == CurrentBitWidth;42}43 44} // namespace45 46NarrowingConversionsCheck::NarrowingConversionsCheck(StringRef Name,47                                                     ClangTidyContext *Context)48    : ClangTidyCheck(Name, Context),49      WarnOnIntegerNarrowingConversion(50          Options.get("WarnOnIntegerNarrowingConversion", true)),51      WarnOnIntegerToFloatingPointNarrowingConversion(52          Options.get("WarnOnIntegerToFloatingPointNarrowingConversion", true)),53      WarnOnFloatingPointNarrowingConversion(54          Options.get("WarnOnFloatingPointNarrowingConversion", true)),55      WarnWithinTemplateInstantiation(56          Options.get("WarnWithinTemplateInstantiation", false)),57      WarnOnEquivalentBitWidth(Options.get("WarnOnEquivalentBitWidth", true)),58      IgnoreConversionFromTypes(Options.get("IgnoreConversionFromTypes", "")),59      PedanticMode(Options.get("PedanticMode", false)) {}60 61void NarrowingConversionsCheck::storeOptions(62    ClangTidyOptions::OptionMap &Opts) {63  Options.store(Opts, "WarnOnIntegerNarrowingConversion",64                WarnOnIntegerNarrowingConversion);65  Options.store(Opts, "WarnOnIntegerToFloatingPointNarrowingConversion",66                WarnOnIntegerToFloatingPointNarrowingConversion);67  Options.store(Opts, "WarnOnFloatingPointNarrowingConversion",68                WarnOnFloatingPointNarrowingConversion);69  Options.store(Opts, "WarnWithinTemplateInstantiation",70                WarnWithinTemplateInstantiation);71  Options.store(Opts, "WarnOnEquivalentBitWidth", WarnOnEquivalentBitWidth);72  Options.store(Opts, "IgnoreConversionFromTypes", IgnoreConversionFromTypes);73  Options.store(Opts, "PedanticMode", PedanticMode);74}75 76void NarrowingConversionsCheck::registerMatchers(MatchFinder *Finder) {77  // ceil() and floor() are guaranteed to return integers, even though the type78  // is not integral.79  const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl(80      hasAnyName("::ceil", "::std::ceil", "::floor", "::std::floor")))));81 82  const std::vector<StringRef> IgnoreConversionFromTypesVec =83      utils::options::parseStringList(IgnoreConversionFromTypes);84 85  // We may want to exclude other types from the checks, such as `size_type`86  // and `difference_type`. These are often used to count elements, represented87  // in 64 bits and assigned to `int`. Rarely are people counting >2B elements.88  const auto IsConversionFromIgnoredType =89      anyOf(hasType(namedDecl(hasAnyName(IgnoreConversionFromTypesVec))),90            allOf(unless(hasType(namedDecl())),91                  hasType(qualType(hasAnyType(IgnoreConversionFromTypesVec)))));92 93  // `IsConversionFromIgnoredType` will ignore narrowing calls from those types,94  // but not expressions that are promoted to an ignored type as a result of a95  // binary expression with one of those types.96  // For example, it will continue to reject:97  // `int narrowed = int_value + container.size()`.98  // We attempt to address common incidents of compound expressions with99  // `IsIgnoredTypeTwoLevelsDeep`, allowing binary expressions that have one100  // operand of the ignored types and the other operand of another integer type.101  const auto IsIgnoredTypeTwoLevelsDeep =102      anyOf(IsConversionFromIgnoredType,103            binaryOperator(hasOperands(IsConversionFromIgnoredType,104                                       hasType(isInteger()))));105 106  // Bitfields are special. Due to integral promotion [conv.prom/5] bitfield107  // member access expressions are frequently wrapped by an implicit cast to108  // `int` if that type can represent all the values of the bitfield.109  //110  // Consider these examples:111  //   struct SmallBitfield { unsigned int id : 4; };112  //   x.id & 1;             (case-1)113  //   x.id & 1u;            (case-2)114  //   x.id << 1u;           (case-3)115  //   (unsigned)x.id << 1;  (case-4)116  //117  // Due to the promotion rules, we would get a warning for case-1. It's118  // debatable how useful this is, but the user at least has a convenient way of119  // //fixing// it by adding the `u` unsigned-suffix to the literal as120  // demonstrated by case-2. However, this won't work for shift operators like121  // the one in case-3. In case of a normal binary operator, both operands122  // contribute to the result type. However, the type of the shift expression is123  // the promoted type of the left operand. One could still suppress this124  // superfluous warning by explicitly casting the bitfield member access as125  // case-4 demonstrates, but why? The compiler already knew that the value from126  // the member access should safely fit into an `int`, why do we have this127  // warning in the first place? So, hereby we suppress this specific scenario.128  //129  // Note that the bitshift operation might invoke unspecified/undefined130  // behavior, but that's another topic, this checker is about detecting131  // conversion-related defects.132  //133  // Example AST for `x.id << 1`:134  //   BinaryOperator 'int' '<<'135  //   |-ImplicitCastExpr 'int' <IntegralCast>136  //   | `-ImplicitCastExpr 'unsigned int' <LValueToRValue>137  //   |   `-MemberExpr 'unsigned int' lvalue bitfield .id138  //   |     `-DeclRefExpr 'SmallBitfield' lvalue ParmVar 'x' 'SmallBitfield'139  //   `-IntegerLiteral 'int' 1140  const auto ImplicitIntWidenedBitfieldValue = implicitCastExpr(141      hasCastKind(CK_IntegralCast), hasType(asString("int")),142      has(castExpr(hasCastKind(CK_LValueToRValue),143                   has(ignoringParens(memberExpr(hasDeclaration(144                       fieldDecl(isBitField(), unless(hasIntBitwidth())))))))));145 146  // Casts:147  //   i = 0.5;148  //   void f(int); f(0.5);149  Finder->addMatcher(150      traverse(TK_AsIs, implicitCastExpr(151                            hasImplicitDestinationType(152                                hasUnqualifiedDesugaredType(builtinType())),153                            hasSourceExpression(hasType(154                                hasUnqualifiedDesugaredType(builtinType()))),155                            unless(hasSourceExpression(IsCeilFloorCallExpr)),156                            unless(hasParent(castExpr())),157                            WarnWithinTemplateInstantiation158                                ? stmt()159                                : stmt(unless(isInTemplateInstantiation())),160                            IgnoreConversionFromTypes.empty()161                                ? castExpr()162                                : castExpr(unless(hasSourceExpression(163                                      IsIgnoredTypeTwoLevelsDeep))),164                            unless(ImplicitIntWidenedBitfieldValue))165                            .bind("cast")),166      this);167 168  // Binary operators:169  //   i += 0.5;170  Finder->addMatcher(171      binaryOperator(172          isAssignmentOperator(),173          hasLHS(expr(hasType(hasUnqualifiedDesugaredType(builtinType())))),174          hasRHS(expr(hasType(hasUnqualifiedDesugaredType(builtinType())))),175          unless(hasRHS(IsCeilFloorCallExpr)),176          WarnWithinTemplateInstantiation177              ? binaryOperator()178              : binaryOperator(unless(isInTemplateInstantiation())),179          IgnoreConversionFromTypes.empty()180              ? binaryOperator()181              : binaryOperator(unless(hasRHS(IsIgnoredTypeTwoLevelsDeep))),182          // The `=` case generates an implicit cast183          // which is covered by the previous matcher.184          unless(hasOperatorName("=")))185          .bind("binary_op"),186      this);187}188 189static const BuiltinType *getBuiltinType(const Expr &E) {190  return E.getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>();191}192 193static QualType getUnqualifiedType(const Expr &E) {194  return E.getType().getUnqualifiedType();195}196 197static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E) {198  if (auto IntegerConstant = E.getIntegerConstantExpr(Ctx))199    return APValue(*IntegerConstant);200  APValue Constant;201  if (Ctx.getLangOpts().CPlusPlus && E.isCXX11ConstantExpr(Ctx, &Constant))202    return Constant;203  return {};204}205 206static bool getIntegerConstantExprValue(const ASTContext &Context,207                                        const Expr &E, llvm::APSInt &Value) {208  APValue Constant = getConstantExprValue(Context, E);209  if (!Constant.isInt())210    return false;211  Value = Constant.getInt();212  return true;213}214 215static bool getFloatingConstantExprValue(const ASTContext &Context,216                                         const Expr &E, llvm::APFloat &Value) {217  APValue Constant = getConstantExprValue(Context, E);218  if (!Constant.isFloat())219    return false;220  Value = Constant.getFloat();221  return true;222}223 224namespace {225 226struct IntegerRange {227  bool contains(const IntegerRange &From) const {228    return llvm::APSInt::compareValues(Lower, From.Lower) <= 0 &&229           llvm::APSInt::compareValues(Upper, From.Upper) >= 0;230  }231 232  bool contains(const llvm::APSInt &Value) const {233    return llvm::APSInt::compareValues(Lower, Value) <= 0 &&234           llvm::APSInt::compareValues(Upper, Value) >= 0;235  }236 237  llvm::APSInt Lower;238  llvm::APSInt Upper;239};240 241} // namespace242 243static IntegerRange createFromType(const ASTContext &Context,244                                   const BuiltinType &T) {245  if (T.isFloatingPoint()) {246    const unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision(247        Context.getFloatTypeSemantics(T.desugar()));248    // Contrary to two's complement integer, floating point values are249    // symmetric and have the same number of positive and negative values.250    // The range of valid integers for a floating point value is:251    // [-2^PrecisionBits, 2^PrecisionBits]252 253    // Values are created with PrecisionBits plus two bits:254    // - One to express the missing negative value of 2's complement255    //   representation.256    // - One for the sign.257    llvm::APSInt UpperValue(PrecisionBits + 2, /*isUnsigned*/ false);258    UpperValue.setBit(PrecisionBits);259    llvm::APSInt LowerValue(PrecisionBits + 2, /*isUnsigned*/ false);260    LowerValue.setBit(PrecisionBits);261    LowerValue.setSignBit();262    return {LowerValue, UpperValue};263  }264  assert(T.isInteger() && "Unexpected builtin type");265  const uint64_t TypeSize = Context.getTypeSize(&T);266  const bool IsUnsignedInteger = T.isUnsignedInteger();267  return {llvm::APSInt::getMinValue(TypeSize, IsUnsignedInteger),268          llvm::APSInt::getMaxValue(TypeSize, IsUnsignedInteger)};269}270 271static bool isWideEnoughToHold(const ASTContext &Context,272                               const BuiltinType &FromType,273                               const BuiltinType &ToType) {274  const IntegerRange FromIntegerRange = createFromType(Context, FromType);275  const IntegerRange ToIntegerRange = createFromType(Context, ToType);276  return ToIntegerRange.contains(FromIntegerRange);277}278 279static bool isWideEnoughToHold(const ASTContext &Context,280                               const llvm::APSInt &IntegerConstant,281                               const BuiltinType &ToType) {282  const IntegerRange ToIntegerRange = createFromType(Context, ToType);283  return ToIntegerRange.contains(IntegerConstant);284}285 286// Returns true iff the floating point constant can be losslessly represented287// by an integer in the given destination type. eg. 2.0 can be accurately288// represented by an int32_t, but neither 2^33 nor 2.001 can.289static bool isFloatExactlyRepresentable(const ASTContext &Context,290                                        const llvm::APFloat &FloatConstant,291                                        const QualType &DestType) {292  const unsigned DestWidth = Context.getIntWidth(DestType);293  const bool DestSigned = DestType->isSignedIntegerOrEnumerationType();294  llvm::APSInt Result = llvm::APSInt(DestWidth, !DestSigned);295  bool IsExact = false;296  const bool Overflows = FloatConstant.convertToInteger(297                             Result, llvm::APFloat::rmTowardZero, &IsExact) &298                         llvm::APFloat::opInvalidOp;299  return !Overflows && IsExact;300}301 302static llvm::SmallString<64> getValueAsString(const llvm::APSInt &Value,303                                              uint64_t HexBits) {304  llvm::SmallString<64> Str;305  Value.toString(Str, 10);306  if (HexBits > 0) {307    Str.append(" (0x");308    llvm::SmallString<32> HexValue;309    Value.toStringUnsigned(HexValue, 16);310    for (size_t I = HexValue.size(); I < (HexBits / 4); ++I)311      Str.append("0");312    Str.append(HexValue);313    Str.append(")");314  }315  return Str;316}317 318bool NarrowingConversionsCheck::isWarningInhibitedByEquivalentSize(319    const ASTContext &Context, const BuiltinType &FromType,320    const BuiltinType &ToType) const {321  // With this option, we don't warn on conversions that have equivalent width322  // in bits. eg. uint32 <-> int32.323  if (!WarnOnEquivalentBitWidth) {324    const uint64_t FromTypeSize = Context.getTypeSize(&FromType);325    const uint64_t ToTypeSize = Context.getTypeSize(&ToType);326    if (FromTypeSize == ToTypeSize) {327      return true;328    }329  }330  return false;331}332 333void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc,334                                               const Expr &Lhs,335                                               const Expr &Rhs) {336  diag(SourceLoc, "narrowing conversion from %0 to %1")337      << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);338}339 340void NarrowingConversionsCheck::diagNarrowTypeToSignedInt(341    SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs) {342  diag(SourceLoc, "narrowing conversion from %0 to signed type %1 is "343                  "implementation-defined")344      << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);345}346 347void NarrowingConversionsCheck::diagNarrowIntegerConstant(348    SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,349    const llvm::APSInt &Value) {350  diag(SourceLoc,351       "narrowing conversion from constant value %0 of type %1 to %2")352      << getValueAsString(Value, /*NoHex*/ 0) << getUnqualifiedType(Rhs)353      << getUnqualifiedType(Lhs);354}355 356void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt(357    SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,358    const llvm::APSInt &Value, const uint64_t HexBits) {359  diag(SourceLoc, "narrowing conversion from constant value %0 of type %1 "360                  "to signed type %2 is implementation-defined")361      << getValueAsString(Value, HexBits) << getUnqualifiedType(Rhs)362      << getUnqualifiedType(Lhs);363}364 365void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc,366                                                   const Expr &Lhs,367                                                   const Expr &Rhs) {368  diag(SourceLoc, "narrowing conversion from constant %0 to %1")369      << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);370}371 372void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc,373                                                 const Expr &Lhs,374                                                 const Expr &Rhs) {375  diag(SourceLoc, "constant value should be of type of type %0 instead of %1")376      << getUnqualifiedType(Lhs) << getUnqualifiedType(Rhs);377}378 379void NarrowingConversionsCheck::diagNarrowTypeOrConstant(380    const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,381    const Expr &Rhs) {382  APValue Constant = getConstantExprValue(Context, Rhs);383  if (Constant.isInt())384    diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, Constant.getInt());385  else if (Constant.isFloat())386    diagNarrowConstant(SourceLoc, Lhs, Rhs);387  else388    diagNarrowType(SourceLoc, Lhs, Rhs);389}390 391void NarrowingConversionsCheck::handleIntegralCast(const ASTContext &Context,392                                                   SourceLocation SourceLoc,393                                                   const Expr &Lhs,394                                                   const Expr &Rhs) {395  if (WarnOnIntegerNarrowingConversion) {396    const BuiltinType *ToType = getBuiltinType(Lhs);397    // From [conv.integral]p7.3.8:398    // Conversions to unsigned integer is well defined so no warning is issued.399    // "The resulting value is the smallest unsigned value equal to the source400    // value modulo 2^n where n is the number of bits used to represent the401    // destination type."402    if (ToType->isUnsignedInteger())403      return;404    const BuiltinType *FromType = getBuiltinType(Rhs);405 406    // With this option, we don't warn on conversions that have equivalent width407    // in bits. eg. uint32 <-> int32.408    if (!WarnOnEquivalentBitWidth) {409      const uint64_t FromTypeSize = Context.getTypeSize(FromType);410      const uint64_t ToTypeSize = Context.getTypeSize(ToType);411      if (FromTypeSize == ToTypeSize)412        return;413    }414 415    llvm::APSInt IntegerConstant;416    if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {417      if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))418        diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs,419                                             IntegerConstant,420                                             Context.getTypeSize(FromType));421      return;422    }423    if (!isWideEnoughToHold(Context, *FromType, *ToType))424      diagNarrowTypeToSignedInt(SourceLoc, Lhs, Rhs);425  }426}427 428void NarrowingConversionsCheck::handleIntegralToBoolean(429    const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,430    const Expr &Rhs) {431  // Conversion from Integral to Bool value is well defined.432 433  // We keep this function (even if it is empty) to make sure that434  // handleImplicitCast and handleBinaryOperator are symmetric in their behavior435  // and handle the same cases.436}437 438void NarrowingConversionsCheck::handleIntegralToFloating(439    const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,440    const Expr &Rhs) {441  if (WarnOnIntegerToFloatingPointNarrowingConversion) {442    const BuiltinType *ToType = getBuiltinType(Lhs);443    llvm::APSInt IntegerConstant;444    if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {445      if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))446        diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant);447      return;448    }449 450    const BuiltinType *FromType = getBuiltinType(Rhs);451    if (isWarningInhibitedByEquivalentSize(Context, *FromType, *ToType))452      return;453    if (!isWideEnoughToHold(Context, *FromType, *ToType))454      diagNarrowType(SourceLoc, Lhs, Rhs);455  }456}457 458void NarrowingConversionsCheck::handleFloatingToIntegral(459    const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,460    const Expr &Rhs) {461  llvm::APFloat FloatConstant(0.0);462  if (getFloatingConstantExprValue(Context, Rhs, FloatConstant)) {463    if (!isFloatExactlyRepresentable(Context, FloatConstant, Lhs.getType()))464      diagNarrowConstant(SourceLoc, Lhs, Rhs);465 466    else if (PedanticMode)467      diagConstantCast(SourceLoc, Lhs, Rhs);468 469    return;470  }471 472  const BuiltinType *FromType = getBuiltinType(Rhs);473  const BuiltinType *ToType = getBuiltinType(Lhs);474  if (isWarningInhibitedByEquivalentSize(Context, *FromType, *ToType))475    return;476  diagNarrowType(SourceLoc, Lhs, Rhs); // Assumed always lossy.477}478 479void NarrowingConversionsCheck::handleFloatingToBoolean(480    const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,481    const Expr &Rhs) {482  diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs);483}484 485void NarrowingConversionsCheck::handleBooleanToSignedIntegral(486    const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,487    const Expr &Rhs) {488  // Conversion from Bool to SignedIntegral value is well defined.489 490  // We keep this function (even if it is empty) to make sure that491  // handleImplicitCast and handleBinaryOperator are symmetric in their behavior492  // and handle the same cases.493}494 495void NarrowingConversionsCheck::handleFloatingCast(const ASTContext &Context,496                                                   SourceLocation SourceLoc,497                                                   const Expr &Lhs,498                                                   const Expr &Rhs) {499  if (WarnOnFloatingPointNarrowingConversion) {500    const BuiltinType *ToType = getBuiltinType(Lhs);501    APValue Constant = getConstantExprValue(Context, Rhs);502    if (Constant.isFloat()) {503      // From [dcl.init.list]p7.2:504      // Floating point constant narrowing only takes place when the value is505      // not within destination range. We convert the value to the destination506      // type and check if the resulting value is infinity.507      llvm::APFloat Tmp = Constant.getFloat();508      bool UnusedLosesInfo = false;509      Tmp.convert(Context.getFloatTypeSemantics(ToType->desugar()),510                  llvm::APFloatBase::rmNearestTiesToEven, &UnusedLosesInfo);511      if (Tmp.isInfinity())512        diagNarrowConstant(SourceLoc, Lhs, Rhs);513      return;514    }515    const BuiltinType *FromType = getBuiltinType(Rhs);516    if (!llvm::APFloatBase::isRepresentableBy(517            Context.getFloatTypeSemantics(FromType->desugar()),518            Context.getFloatTypeSemantics(ToType->desugar())))519      diagNarrowType(SourceLoc, Lhs, Rhs);520  }521}522 523void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,524                                                     SourceLocation SourceLoc,525                                                     const Expr &Lhs,526                                                     const Expr &Rhs) {527  assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() &&528         "Dependent types must be check before calling this function");529  const BuiltinType *LhsType = getBuiltinType(Lhs);530  const BuiltinType *RhsType = getBuiltinType(Rhs);531  if (RhsType == nullptr || LhsType == nullptr)532    return;533  if (LhsType == RhsType)534    return;535  if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger())536    handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);537  else if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool)538    handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);539  else if (RhsType->isInteger() && LhsType->isFloatingPoint())540    handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);541  else if (RhsType->isInteger() && LhsType->isInteger())542    handleIntegralCast(Context, SourceLoc, Lhs, Rhs);543  else if (RhsType->isFloatingPoint() &&544           LhsType->getKind() == BuiltinType::Bool)545    handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);546  else if (RhsType->isFloatingPoint() && LhsType->isInteger())547    handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);548  else if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint())549    handleFloatingCast(Context, SourceLoc, Lhs, Rhs);550}551 552bool NarrowingConversionsCheck::handleConditionalOperator(553    const ASTContext &Context, const Expr &Lhs, const Expr &Rhs) {554  if (const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) {555    // We have an expression like so: `output = cond ? lhs : rhs`556    // From the point of view of narrowing conversion we treat it as two557    // expressions `output = lhs` and `output = rhs`.558    handleConditionalOperatorArgument(Context, Lhs, CO->getLHS());559    handleConditionalOperatorArgument(Context, Lhs, CO->getRHS());560    return true;561  }562  return false;563}564 565void NarrowingConversionsCheck::handleConditionalOperatorArgument(566    const ASTContext &Context, const Expr &Lhs, const Expr *Arg) {567  if (const auto *ICE = llvm::dyn_cast<ImplicitCastExpr>(Arg))568    if (!Arg->getIntegerConstantExpr(Context))569      Arg = ICE->getSubExpr();570 571  handleBinaryOperator(Context, Arg->getExprLoc(), Lhs, *Arg);572}573 574void NarrowingConversionsCheck::handleImplicitCast(575    const ASTContext &Context, const ImplicitCastExpr &Cast) {576  if (Cast.getExprLoc().isMacroID())577    return;578  const Expr &Lhs = Cast;579  const Expr &Rhs = *Cast.getSubExpr();580  if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())581    return;582  if (getBuiltinType(Lhs) == getBuiltinType(Rhs))583    return;584  if (handleConditionalOperator(Context, Lhs, Rhs))585    return;586  const SourceLocation SourceLoc = Lhs.getExprLoc();587  switch (Cast.getCastKind()) {588  case CK_BooleanToSignedIntegral:589    handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);590    return;591  case CK_IntegralToBoolean:592    handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);593    return;594  case CK_IntegralToFloating:595    handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);596    return;597  case CK_IntegralCast:598    handleIntegralCast(Context, SourceLoc, Lhs, Rhs);599    return;600  case CK_FloatingToBoolean:601    handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);602    return;603  case CK_FloatingToIntegral:604    handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);605    return;606  case CK_FloatingCast:607    handleFloatingCast(Context, SourceLoc, Lhs, Rhs);608    return;609  default:610    return;611  }612}613 614void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,615                                                     const BinaryOperator &Op) {616  if (Op.getBeginLoc().isMacroID())617    return;618  const Expr &Lhs = *Op.getLHS();619  const Expr &Rhs = *Op.getRHS();620  if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())621    return;622  if (handleConditionalOperator(Context, Lhs, Rhs))623    return;624  handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs);625}626 627void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) {628  if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("binary_op"))629    handleBinaryOperator(*Result.Context, *Op);630  else if (const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast"))631    handleImplicitCast(*Result.Context, *Cast);632  else633    llvm_unreachable("must be binary operator or cast expression");634}635} // namespace clang::tidy::bugprone636