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