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