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