1 //===--- NarrowingConversionsCheck.cpp - clang-tidy------------------------===// 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-exception 6 // 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 #include "llvm/ADT/SmallVector.h" 20 21 #include <cstdint> 22 23 using namespace clang::ast_matchers; 24 25 namespace clang::tidy::bugprone { 26 27 namespace { 28 29 AST_MATCHER_P(QualType, hasAnyType, std::vector<StringRef>, Names) { 30 if (Names.empty()) 31 return false; 32 33 std::string Name = Node.getLocalUnqualifiedType().getAsString(); 34 return llvm::any_of(Names, [&Name](StringRef Ref) { return Ref == Name; }); 35 } 36 37 AST_MATCHER(FieldDecl, hasIntBitwidth) { 38 assert(Node.isBitField()); 39 const ASTContext &Ctx = Node.getASTContext(); 40 unsigned IntBitWidth = Ctx.getIntWidth(Ctx.IntTy); 41 unsigned CurrentBitWidth = Node.getBitWidthValue(); 42 return IntBitWidth == CurrentBitWidth; 43 } 44 45 } // namespace 46 47 NarrowingConversionsCheck::NarrowingConversionsCheck(StringRef Name, 48 ClangTidyContext *Context) 49 : ClangTidyCheck(Name, Context), 50 WarnOnIntegerNarrowingConversion( 51 Options.get("WarnOnIntegerNarrowingConversion", true)), 52 WarnOnIntegerToFloatingPointNarrowingConversion( 53 Options.get("WarnOnIntegerToFloatingPointNarrowingConversion", true)), 54 WarnOnFloatingPointNarrowingConversion( 55 Options.get("WarnOnFloatingPointNarrowingConversion", true)), 56 WarnWithinTemplateInstantiation( 57 Options.get("WarnWithinTemplateInstantiation", false)), 58 WarnOnEquivalentBitWidth(Options.get("WarnOnEquivalentBitWidth", true)), 59 IgnoreConversionFromTypes(Options.get("IgnoreConversionFromTypes", "")), 60 PedanticMode(Options.get("PedanticMode", false)) {} 61 62 void NarrowingConversionsCheck::storeOptions( 63 ClangTidyOptions::OptionMap &Opts) { 64 Options.store(Opts, "WarnOnIntegerNarrowingConversion", 65 WarnOnIntegerNarrowingConversion); 66 Options.store(Opts, "WarnOnIntegerToFloatingPointNarrowingConversion", 67 WarnOnIntegerToFloatingPointNarrowingConversion); 68 Options.store(Opts, "WarnOnFloatingPointNarrowingConversion", 69 WarnOnFloatingPointNarrowingConversion); 70 Options.store(Opts, "WarnWithinTemplateInstantiation", 71 WarnWithinTemplateInstantiation); 72 Options.store(Opts, "WarnOnEquivalentBitWidth", WarnOnEquivalentBitWidth); 73 Options.store(Opts, "IgnoreConversionFromTypes", IgnoreConversionFromTypes); 74 Options.store(Opts, "PedanticMode", PedanticMode); 75 } 76 77 void NarrowingConversionsCheck::registerMatchers(MatchFinder *Finder) { 78 // ceil() and floor() are guaranteed to return integers, even though the type 79 // is not integral. 80 const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl( 81 hasAnyName("::ceil", "::std::ceil", "::floor", "::std::floor"))))); 82 83 std::vector<StringRef> IgnoreConversionFromTypesVec = 84 utils::options::parseStringList(IgnoreConversionFromTypes); 85 86 // We may want to exclude other types from the checks, such as `size_type` 87 // and `difference_type`. These are often used to count elements, represented 88 // in 64 bits and assigned to `int`. Rarely are people counting >2B elements. 89 const auto IsConversionFromIgnoredType = 90 anyOf(hasType(namedDecl(hasAnyName(IgnoreConversionFromTypesVec))), 91 allOf(unless(hasType(namedDecl())), 92 hasType(qualType(hasAnyType(IgnoreConversionFromTypesVec))))); 93 94 // `IsConversionFromIgnoredType` will ignore narrowing calls from those types, 95 // but not expressions that are promoted to an ignored type as a result of a 96 // binary expression with one of those types. 97 // For example, it will continue to reject: 98 // `int narrowed = int_value + container.size()`. 99 // We attempt to address common incidents of compound expressions with 100 // `IsIgnoredTypeTwoLevelsDeep`, allowing binary expressions that have one 101 // operand of the ignored types and the other operand of another integer type. 102 const auto IsIgnoredTypeTwoLevelsDeep = 103 anyOf(IsConversionFromIgnoredType, 104 binaryOperator(hasOperands(IsConversionFromIgnoredType, 105 hasType(isInteger())))); 106 107 // Bitfields are special. Due to integral promotion [conv.prom/5] bitfield 108 // member access expressions are frequently wrapped by an implicit cast to 109 // `int` if that type can represent all the values of the bitfield. 110 // 111 // Consider these examples: 112 // struct SmallBitfield { unsigned int id : 4; }; 113 // x.id & 1; (case-1) 114 // x.id & 1u; (case-2) 115 // x.id << 1u; (case-3) 116 // (unsigned)x.id << 1; (case-4) 117 // 118 // Due to the promotion rules, we would get a warning for case-1. It's 119 // debatable how useful this is, but the user at least has a convenient way of 120 // //fixing// it by adding the `u` unsigned-suffix to the literal as 121 // demonstrated by case-2. However, this won't work for shift operators like 122 // the one in case-3. In case of a normal binary operator, both operands 123 // contribute to the result type. However, the type of the shift expression is 124 // the promoted type of the left operand. One could still suppress this 125 // superfluous warning by explicitly casting the bitfield member access as 126 // case-4 demonstrates, but why? The compiler already knew that the value from 127 // the member access should safely fit into an `int`, why do we have this 128 // warning in the first place? So, hereby we suppress this specific scenario. 129 // 130 // Note that the bitshift operation might invoke unspecified/undefined 131 // behavior, but that's another topic, this checker is about detecting 132 // conversion-related defects. 133 // 134 // Example AST for `x.id << 1`: 135 // BinaryOperator 'int' '<<' 136 // |-ImplicitCastExpr 'int' <IntegralCast> 137 // | `-ImplicitCastExpr 'unsigned int' <LValueToRValue> 138 // | `-MemberExpr 'unsigned int' lvalue bitfield .id 139 // | `-DeclRefExpr 'SmallBitfield' lvalue ParmVar 'x' 'SmallBitfield' 140 // `-IntegerLiteral 'int' 1 141 const auto ImplicitIntWidenedBitfieldValue = implicitCastExpr( 142 hasCastKind(CK_IntegralCast), hasType(asString("int")), 143 has(castExpr(hasCastKind(CK_LValueToRValue), 144 has(ignoringParens(memberExpr(hasDeclaration( 145 fieldDecl(isBitField(), unless(hasIntBitwidth()))))))))); 146 147 // Casts: 148 // i = 0.5; 149 // void f(int); f(0.5); 150 Finder->addMatcher( 151 traverse(TK_AsIs, implicitCastExpr( 152 hasImplicitDestinationType( 153 hasUnqualifiedDesugaredType(builtinType())), 154 hasSourceExpression(hasType( 155 hasUnqualifiedDesugaredType(builtinType()))), 156 unless(hasSourceExpression(IsCeilFloorCallExpr)), 157 unless(hasParent(castExpr())), 158 WarnWithinTemplateInstantiation 159 ? stmt() 160 : stmt(unless(isInTemplateInstantiation())), 161 IgnoreConversionFromTypes.empty() 162 ? castExpr() 163 : castExpr(unless(hasSourceExpression( 164 IsIgnoredTypeTwoLevelsDeep))), 165 unless(ImplicitIntWidenedBitfieldValue)) 166 .bind("cast")), 167 this); 168 169 // Binary operators: 170 // i += 0.5; 171 Finder->addMatcher( 172 binaryOperator( 173 isAssignmentOperator(), 174 hasLHS(expr(hasType(hasUnqualifiedDesugaredType(builtinType())))), 175 hasRHS(expr(hasType(hasUnqualifiedDesugaredType(builtinType())))), 176 unless(hasRHS(IsCeilFloorCallExpr)), 177 WarnWithinTemplateInstantiation 178 ? binaryOperator() 179 : binaryOperator(unless(isInTemplateInstantiation())), 180 IgnoreConversionFromTypes.empty() 181 ? binaryOperator() 182 : binaryOperator(unless(hasRHS(IsIgnoredTypeTwoLevelsDeep))), 183 // The `=` case generates an implicit cast 184 // which is covered by the previous matcher. 185 unless(hasOperatorName("="))) 186 .bind("binary_op"), 187 this); 188 } 189 190 static const BuiltinType *getBuiltinType(const Expr &E) { 191 return E.getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>(); 192 } 193 194 static QualType getUnqualifiedType(const Expr &E) { 195 return E.getType().getUnqualifiedType(); 196 } 197 198 static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E) { 199 if (auto IntegerConstant = E.getIntegerConstantExpr(Ctx)) 200 return APValue(*IntegerConstant); 201 APValue Constant; 202 if (Ctx.getLangOpts().CPlusPlus && E.isCXX11ConstantExpr(Ctx, &Constant)) 203 return Constant; 204 return {}; 205 } 206 207 static bool getIntegerConstantExprValue(const ASTContext &Context, 208 const Expr &E, llvm::APSInt &Value) { 209 APValue Constant = getConstantExprValue(Context, E); 210 if (!Constant.isInt()) 211 return false; 212 Value = Constant.getInt(); 213 return true; 214 } 215 216 static bool getFloatingConstantExprValue(const ASTContext &Context, 217 const Expr &E, llvm::APFloat &Value) { 218 APValue Constant = getConstantExprValue(Context, E); 219 if (!Constant.isFloat()) 220 return false; 221 Value = Constant.getFloat(); 222 return true; 223 } 224 225 namespace { 226 227 struct IntegerRange { 228 bool contains(const IntegerRange &From) const { 229 return llvm::APSInt::compareValues(Lower, From.Lower) <= 0 && 230 llvm::APSInt::compareValues(Upper, From.Upper) >= 0; 231 } 232 233 bool contains(const llvm::APSInt &Value) const { 234 return llvm::APSInt::compareValues(Lower, Value) <= 0 && 235 llvm::APSInt::compareValues(Upper, Value) >= 0; 236 } 237 238 llvm::APSInt Lower; 239 llvm::APSInt Upper; 240 }; 241 242 } // namespace 243 244 static IntegerRange createFromType(const ASTContext &Context, 245 const BuiltinType &T) { 246 if (T.isFloatingPoint()) { 247 unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision( 248 Context.getFloatTypeSemantics(T.desugar())); 249 // Contrary to two's complement integer, floating point values are 250 // symmetric and have the same number of positive and negative values. 251 // The range of valid integers for a floating point value is: 252 // [-2^PrecisionBits, 2^PrecisionBits] 253 254 // Values are created with PrecisionBits plus two bits: 255 // - One to express the missing negative value of 2's complement 256 // representation. 257 // - One for the sign. 258 llvm::APSInt UpperValue(PrecisionBits + 2, /*isUnsigned*/ false); 259 UpperValue.setBit(PrecisionBits); 260 llvm::APSInt LowerValue(PrecisionBits + 2, /*isUnsigned*/ false); 261 LowerValue.setBit(PrecisionBits); 262 LowerValue.setSignBit(); 263 return {LowerValue, UpperValue}; 264 } 265 assert(T.isInteger() && "Unexpected builtin type"); 266 uint64_t TypeSize = Context.getTypeSize(&T); 267 bool IsUnsignedInteger = T.isUnsignedInteger(); 268 return {llvm::APSInt::getMinValue(TypeSize, IsUnsignedInteger), 269 llvm::APSInt::getMaxValue(TypeSize, IsUnsignedInteger)}; 270 } 271 272 static bool isWideEnoughToHold(const ASTContext &Context, 273 const BuiltinType &FromType, 274 const BuiltinType &ToType) { 275 IntegerRange FromIntegerRange = createFromType(Context, FromType); 276 IntegerRange ToIntegerRange = createFromType(Context, ToType); 277 return ToIntegerRange.contains(FromIntegerRange); 278 } 279 280 static bool isWideEnoughToHold(const ASTContext &Context, 281 const llvm::APSInt &IntegerConstant, 282 const BuiltinType &ToType) { 283 IntegerRange ToIntegerRange = createFromType(Context, ToType); 284 return ToIntegerRange.contains(IntegerConstant); 285 } 286 287 // Returns true iff the floating point constant can be losslessly represented 288 // by an integer in the given destination type. eg. 2.0 can be accurately 289 // represented by an int32_t, but neither 2^33 nor 2.001 can. 290 static bool isFloatExactlyRepresentable(const ASTContext &Context, 291 const llvm::APFloat &FloatConstant, 292 const QualType &DestType) { 293 unsigned DestWidth = Context.getIntWidth(DestType); 294 bool DestSigned = DestType->isSignedIntegerOrEnumerationType(); 295 llvm::APSInt Result = llvm::APSInt(DestWidth, !DestSigned); 296 bool IsExact = false; 297 bool Overflows = FloatConstant.convertToInteger( 298 Result, llvm::APFloat::rmTowardZero, &IsExact) & 299 llvm::APFloat::opInvalidOp; 300 return !Overflows && IsExact; 301 } 302 303 static llvm::SmallString<64> getValueAsString(const llvm::APSInt &Value, 304 uint64_t HexBits) { 305 llvm::SmallString<64> Str; 306 Value.toString(Str, 10); 307 if (HexBits > 0) { 308 Str.append(" (0x"); 309 llvm::SmallString<32> HexValue; 310 Value.toStringUnsigned(HexValue, 16); 311 for (size_t I = HexValue.size(); I < (HexBits / 4); ++I) 312 Str.append("0"); 313 Str.append(HexValue); 314 Str.append(")"); 315 } 316 return Str; 317 } 318 319 bool NarrowingConversionsCheck::isWarningInhibitedByEquivalentSize( 320 const ASTContext &Context, const BuiltinType &FromType, 321 const BuiltinType &ToType) const { 322 // With this option, we don't warn on conversions that have equivalent width 323 // in bits. eg. uint32 <-> int32. 324 if (!WarnOnEquivalentBitWidth) { 325 uint64_t FromTypeSize = Context.getTypeSize(&FromType); 326 uint64_t ToTypeSize = Context.getTypeSize(&ToType); 327 if (FromTypeSize == ToTypeSize) { 328 return true; 329 } 330 } 331 return false; 332 } 333 334 void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc, 335 const Expr &Lhs, 336 const Expr &Rhs) { 337 diag(SourceLoc, "narrowing conversion from %0 to %1") 338 << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs); 339 } 340 341 void NarrowingConversionsCheck::diagNarrowTypeToSignedInt( 342 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs) { 343 diag(SourceLoc, "narrowing conversion from %0 to signed type %1 is " 344 "implementation-defined") 345 << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs); 346 } 347 348 void NarrowingConversionsCheck::diagNarrowIntegerConstant( 349 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs, 350 const llvm::APSInt &Value) { 351 diag(SourceLoc, 352 "narrowing conversion from constant value %0 of type %1 to %2") 353 << getValueAsString(Value, /*NoHex*/ 0) << getUnqualifiedType(Rhs) 354 << getUnqualifiedType(Lhs); 355 } 356 357 void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt( 358 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs, 359 const llvm::APSInt &Value, const uint64_t HexBits) { 360 diag(SourceLoc, "narrowing conversion from constant value %0 of type %1 " 361 "to signed type %2 is implementation-defined") 362 << getValueAsString(Value, HexBits) << getUnqualifiedType(Rhs) 363 << getUnqualifiedType(Lhs); 364 } 365 366 void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc, 367 const Expr &Lhs, 368 const Expr &Rhs) { 369 diag(SourceLoc, "narrowing conversion from constant %0 to %1") 370 << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs); 371 } 372 373 void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc, 374 const Expr &Lhs, 375 const Expr &Rhs) { 376 diag(SourceLoc, "constant value should be of type of type %0 instead of %1") 377 << getUnqualifiedType(Lhs) << getUnqualifiedType(Rhs); 378 } 379 380 void NarrowingConversionsCheck::diagNarrowTypeOrConstant( 381 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs, 382 const Expr &Rhs) { 383 APValue Constant = getConstantExprValue(Context, Rhs); 384 if (Constant.isInt()) 385 return diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, Constant.getInt()); 386 if (Constant.isFloat()) 387 return diagNarrowConstant(SourceLoc, Lhs, Rhs); 388 return diagNarrowType(SourceLoc, Lhs, Rhs); 389 } 390 391 void 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 source 400 // value modulo 2^n where n is the number of bits used to represent the 401 // 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 width 407 // in bits. eg. uint32 <-> int32. 408 if (!WarnOnEquivalentBitWidth) { 409 uint64_t FromTypeSize = Context.getTypeSize(FromType); 410 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 428 void 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 that 434 // handleImplicitCast and handleBinaryOperator are symmetric in their behavior 435 // and handle the same cases. 436 } 437 438 void 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 458 void 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 return diagNarrowConstant(SourceLoc, Lhs, Rhs); 465 466 if (PedanticMode) 467 return 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 479 void NarrowingConversionsCheck::handleFloatingToBoolean( 480 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs, 481 const Expr &Rhs) { 482 return diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs); 483 } 484 485 void 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 that 491 // handleImplicitCast and handleBinaryOperator are symmetric in their behavior 492 // and handle the same cases. 493 } 494 495 void 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 is 505 // not within destination range. We convert the value to the destination 506 // 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 523 void 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 return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs); 537 if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool) 538 return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs); 539 if (RhsType->isInteger() && LhsType->isFloatingPoint()) 540 return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs); 541 if (RhsType->isInteger() && LhsType->isInteger()) 542 return handleIntegralCast(Context, SourceLoc, Lhs, Rhs); 543 if (RhsType->isFloatingPoint() && LhsType->getKind() == BuiltinType::Bool) 544 return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs); 545 if (RhsType->isFloatingPoint() && LhsType->isInteger()) 546 return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs); 547 if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint()) 548 return handleFloatingCast(Context, SourceLoc, Lhs, Rhs); 549 } 550 551 bool NarrowingConversionsCheck::handleConditionalOperator( 552 const ASTContext &Context, const Expr &Lhs, const Expr &Rhs) { 553 if (const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) { 554 // We have an expression like so: `output = cond ? lhs : rhs` 555 // From the point of view of narrowing conversion we treat it as two 556 // expressions `output = lhs` and `output = rhs`. 557 handleBinaryOperator(Context, CO->getLHS()->getExprLoc(), Lhs, 558 *CO->getLHS()); 559 handleBinaryOperator(Context, CO->getRHS()->getExprLoc(), Lhs, 560 *CO->getRHS()); 561 return true; 562 } 563 return false; 564 } 565 566 void NarrowingConversionsCheck::handleImplicitCast( 567 const ASTContext &Context, const ImplicitCastExpr &Cast) { 568 if (Cast.getExprLoc().isMacroID()) 569 return; 570 const Expr &Lhs = Cast; 571 const Expr &Rhs = *Cast.getSubExpr(); 572 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent()) 573 return; 574 if (getBuiltinType(Lhs) == getBuiltinType(Rhs)) 575 return; 576 if (handleConditionalOperator(Context, Lhs, Rhs)) 577 return; 578 SourceLocation SourceLoc = Lhs.getExprLoc(); 579 switch (Cast.getCastKind()) { 580 case CK_BooleanToSignedIntegral: 581 return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs); 582 case CK_IntegralToBoolean: 583 return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs); 584 case CK_IntegralToFloating: 585 return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs); 586 case CK_IntegralCast: 587 return handleIntegralCast(Context, SourceLoc, Lhs, Rhs); 588 case CK_FloatingToBoolean: 589 return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs); 590 case CK_FloatingToIntegral: 591 return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs); 592 case CK_FloatingCast: 593 return handleFloatingCast(Context, SourceLoc, Lhs, Rhs); 594 default: 595 break; 596 } 597 } 598 599 void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context, 600 const BinaryOperator &Op) { 601 if (Op.getBeginLoc().isMacroID()) 602 return; 603 const Expr &Lhs = *Op.getLHS(); 604 const Expr &Rhs = *Op.getRHS(); 605 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent()) 606 return; 607 if (handleConditionalOperator(Context, Lhs, Rhs)) 608 return; 609 handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs); 610 } 611 612 void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) { 613 if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("binary_op")) 614 return handleBinaryOperator(*Result.Context, *Op); 615 if (const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast")) 616 return handleImplicitCast(*Result.Context, *Cast); 617 llvm_unreachable("must be binary operator or cast expression"); 618 } 619 } // namespace clang::tidy::bugprone 620