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