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