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