1 //===--- NarrowingConversionsCheck.cpp - clang-tidy------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "NarrowingConversionsCheck.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/AST/Type.h" 13 #include "clang/ASTMatchers/ASTMatchFinder.h" 14 #include "llvm/ADT/APSInt.h" 15 #include "llvm/ADT/SmallString.h" 16 #include "llvm/ADT/SmallVector.h" 17 18 #include <cstdint> 19 20 using namespace clang::ast_matchers; 21 22 namespace clang { 23 namespace tidy { 24 namespace cppcoreguidelines { 25 26 NarrowingConversionsCheck::NarrowingConversionsCheck(StringRef Name, 27 ClangTidyContext *Context) 28 : ClangTidyCheck(Name, Context), 29 WarnOnFloatingPointNarrowingConversion( 30 Options.get("WarnOnFloatingPointNarrowingConversion", 1)), 31 PedanticMode(Options.get("PedanticMode", 0)) {} 32 33 void NarrowingConversionsCheck::registerMatchers(MatchFinder *Finder) { 34 // ceil() and floor() are guaranteed to return integers, even though the type 35 // is not integral. 36 const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl( 37 hasAnyName("::ceil", "::std::ceil", "::floor", "::std::floor"))))); 38 39 // Casts: 40 // i = 0.5; 41 // void f(int); f(0.5); 42 Finder->addMatcher( 43 implicitCastExpr(hasImplicitDestinationType(builtinType()), 44 hasSourceExpression(hasType(builtinType())), 45 unless(hasSourceExpression(IsCeilFloorCallExpr)), 46 unless(hasParent(castExpr())), 47 unless(isInTemplateInstantiation())) 48 .bind("cast"), 49 this); 50 51 // Binary operators: 52 // i += 0.5; 53 Finder->addMatcher(binaryOperator(isAssignmentOperator(), 54 hasLHS(expr(hasType(builtinType()))), 55 hasRHS(expr(hasType(builtinType()))), 56 unless(hasRHS(IsCeilFloorCallExpr)), 57 unless(isInTemplateInstantiation()), 58 // The `=` case generates an implicit cast 59 // which is covered by the previous matcher. 60 unless(hasOperatorName("="))) 61 .bind("binary_op"), 62 this); 63 } 64 65 static const BuiltinType *getBuiltinType(const Expr &E) { 66 return E.getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>(); 67 } 68 69 static QualType getUnqualifiedType(const Expr &E) { 70 return E.getType().getUnqualifiedType(); 71 } 72 73 static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E) { 74 llvm::APSInt IntegerConstant; 75 if (E.isIntegerConstantExpr(IntegerConstant, Ctx)) 76 return APValue(IntegerConstant); 77 APValue Constant; 78 if (Ctx.getLangOpts().CPlusPlus && E.isCXX11ConstantExpr(Ctx, &Constant)) 79 return Constant; 80 return {}; 81 } 82 83 static bool getIntegerConstantExprValue(const ASTContext &Context, 84 const Expr &E, llvm::APSInt &Value) { 85 APValue Constant = getConstantExprValue(Context, E); 86 if (!Constant.isInt()) 87 return false; 88 Value = Constant.getInt(); 89 return true; 90 } 91 92 static bool getFloatingConstantExprValue(const ASTContext &Context, 93 const Expr &E, llvm::APFloat &Value) { 94 APValue Constant = getConstantExprValue(Context, E); 95 if (!Constant.isFloat()) 96 return false; 97 Value = Constant.getFloat(); 98 return true; 99 } 100 101 namespace { 102 103 struct IntegerRange { 104 bool Contains(const IntegerRange &From) const { 105 return llvm::APSInt::compareValues(Lower, From.Lower) <= 0 && 106 llvm::APSInt::compareValues(Upper, From.Upper) >= 0; 107 } 108 109 bool Contains(const llvm::APSInt &Value) const { 110 return llvm::APSInt::compareValues(Lower, Value) <= 0 && 111 llvm::APSInt::compareValues(Upper, Value) >= 0; 112 } 113 114 llvm::APSInt Lower; 115 llvm::APSInt Upper; 116 }; 117 118 } // namespace 119 120 static IntegerRange createFromType(const ASTContext &Context, 121 const BuiltinType &T) { 122 if (T.isFloatingPoint()) { 123 unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision( 124 Context.getFloatTypeSemantics(T.desugar())); 125 // Contrary to two's complement integer, floating point values are 126 // symmetric and have the same number of positive and negative values. 127 // The range of valid integers for a floating point value is: 128 // [-2^PrecisionBits, 2^PrecisionBits] 129 130 // Values are created with PrecisionBits plus two bits: 131 // - One to express the missing negative value of 2's complement 132 // representation. 133 // - One for the sign. 134 llvm::APSInt UpperValue(PrecisionBits + 2, /*isUnsigned*/ false); 135 UpperValue.setBit(PrecisionBits); 136 llvm::APSInt LowerValue(PrecisionBits + 2, /*isUnsigned*/ false); 137 LowerValue.setBit(PrecisionBits); 138 LowerValue.setSignBit(); 139 return {LowerValue, UpperValue}; 140 } 141 assert(T.isInteger() && "Unexpected builtin type"); 142 uint64_t TypeSize = Context.getTypeSize(&T); 143 bool IsUnsignedInteger = T.isUnsignedInteger(); 144 return {llvm::APSInt::getMinValue(TypeSize, IsUnsignedInteger), 145 llvm::APSInt::getMaxValue(TypeSize, IsUnsignedInteger)}; 146 } 147 148 static bool isWideEnoughToHold(const ASTContext &Context, 149 const BuiltinType &FromType, 150 const BuiltinType &ToType) { 151 IntegerRange FromIntegerRange = createFromType(Context, FromType); 152 IntegerRange ToIntegerRange = createFromType(Context, ToType); 153 return ToIntegerRange.Contains(FromIntegerRange); 154 } 155 156 static bool isWideEnoughToHold(const ASTContext &Context, 157 const llvm::APSInt &IntegerConstant, 158 const BuiltinType &ToType) { 159 IntegerRange ToIntegerRange = createFromType(Context, ToType); 160 return ToIntegerRange.Contains(IntegerConstant); 161 } 162 163 static llvm::SmallString<64> getValueAsString(const llvm::APSInt &Value, 164 uint64_t HexBits) { 165 llvm::SmallString<64> Str; 166 Value.toString(Str, 10); 167 if (HexBits > 0) { 168 Str.append(" (0x"); 169 llvm::SmallString<32> HexValue; 170 Value.toStringUnsigned(HexValue, 16); 171 for (size_t I = HexValue.size(); I < (HexBits / 4); ++I) 172 Str.append("0"); 173 Str.append(HexValue); 174 Str.append(")"); 175 } 176 return Str; 177 } 178 179 void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc, 180 const Expr &Lhs, 181 const Expr &Rhs) { 182 diag(SourceLoc, "narrowing conversion from %0 to %1") 183 << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs); 184 } 185 186 void NarrowingConversionsCheck::diagNarrowTypeToSignedInt( 187 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs) { 188 diag(SourceLoc, "narrowing conversion from %0 to signed type %1 is " 189 "implementation-defined") 190 << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs); 191 } 192 193 void NarrowingConversionsCheck::diagNarrowIntegerConstant( 194 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs, 195 const llvm::APSInt &Value) { 196 diag(SourceLoc, 197 "narrowing conversion from constant value %0 of type %1 to %2") 198 << getValueAsString(Value, /*NoHex*/ 0) << getUnqualifiedType(Rhs) 199 << getUnqualifiedType(Lhs); 200 } 201 202 void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt( 203 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs, 204 const llvm::APSInt &Value, const uint64_t HexBits) { 205 diag(SourceLoc, "narrowing conversion from constant value %0 of type %1 " 206 "to signed type %2 is implementation-defined") 207 << getValueAsString(Value, HexBits) << getUnqualifiedType(Rhs) 208 << getUnqualifiedType(Lhs); 209 } 210 211 void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc, 212 const Expr &Lhs, 213 const Expr &Rhs) { 214 diag(SourceLoc, "narrowing conversion from constant %0 to %1") 215 << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs); 216 } 217 218 void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc, 219 const Expr &Lhs, 220 const Expr &Rhs) { 221 diag(SourceLoc, "constant value should be of type of type %0 instead of %1") 222 << getUnqualifiedType(Lhs) << getUnqualifiedType(Rhs); 223 } 224 225 void NarrowingConversionsCheck::diagNarrowTypeOrConstant( 226 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs, 227 const Expr &Rhs) { 228 APValue Constant = getConstantExprValue(Context, Rhs); 229 if (Constant.isInt()) 230 return diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, Constant.getInt()); 231 if (Constant.isFloat()) 232 return diagNarrowConstant(SourceLoc, Lhs, Rhs); 233 return diagNarrowType(SourceLoc, Lhs, Rhs); 234 } 235 236 void NarrowingConversionsCheck::handleIntegralCast(const ASTContext &Context, 237 SourceLocation SourceLoc, 238 const Expr &Lhs, 239 const Expr &Rhs) { 240 const BuiltinType *ToType = getBuiltinType(Lhs); 241 // From [conv.integral]p7.3.8: 242 // Conversions to unsigned integer is well defined so no warning is issued. 243 // "The resulting value is the smallest unsigned value equal to the source 244 // value modulo 2^n where n is the number of bits used to represent the 245 // destination type." 246 if (ToType->isUnsignedInteger()) 247 return; 248 const BuiltinType *FromType = getBuiltinType(Rhs); 249 llvm::APSInt IntegerConstant; 250 if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) { 251 if (!isWideEnoughToHold(Context, IntegerConstant, *ToType)) 252 diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs, IntegerConstant, 253 Context.getTypeSize(FromType)); 254 return; 255 } 256 if (!isWideEnoughToHold(Context, *FromType, *ToType)) 257 diagNarrowTypeToSignedInt(SourceLoc, Lhs, Rhs); 258 } 259 260 void NarrowingConversionsCheck::handleIntegralToBoolean( 261 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs, 262 const Expr &Rhs) { 263 // Conversion from Integral to Bool value is well defined. 264 265 // We keep this function (even if it is empty) to make sure that 266 // handleImplicitCast and handleBinaryOperator are symmetric in their behavior 267 // and handle the same cases. 268 } 269 270 void NarrowingConversionsCheck::handleIntegralToFloating( 271 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs, 272 const Expr &Rhs) { 273 const BuiltinType *ToType = getBuiltinType(Lhs); 274 llvm::APSInt IntegerConstant; 275 if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) { 276 if (!isWideEnoughToHold(Context, IntegerConstant, *ToType)) 277 diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant); 278 return; 279 } 280 const BuiltinType *FromType = getBuiltinType(Rhs); 281 if (!isWideEnoughToHold(Context, *FromType, *ToType)) 282 diagNarrowType(SourceLoc, Lhs, Rhs); 283 } 284 285 void NarrowingConversionsCheck::handleFloatingToIntegral( 286 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs, 287 const Expr &Rhs) { 288 llvm::APFloat FloatConstant(0.0); 289 290 // We always warn when Rhs is non-constexpr. 291 if (!getFloatingConstantExprValue(Context, Rhs, FloatConstant)) 292 return diagNarrowType(SourceLoc, Lhs, Rhs); 293 294 QualType DestType = Lhs.getType(); 295 unsigned DestWidth = Context.getIntWidth(DestType); 296 bool DestSigned = DestType->isSignedIntegerOrEnumerationType(); 297 llvm::APSInt Result = llvm::APSInt(DestWidth, !DestSigned); 298 bool IsExact = false; 299 bool Overflows = FloatConstant.convertToInteger( 300 Result, llvm::APFloat::rmTowardZero, &IsExact) & 301 llvm::APFloat::opInvalidOp; 302 // We warn iff the constant floating point value is not exactly representable. 303 if (Overflows || !IsExact) 304 return diagNarrowConstant(SourceLoc, Lhs, Rhs); 305 306 if (PedanticMode) 307 return diagConstantCast(SourceLoc, Lhs, Rhs); 308 } 309 310 void NarrowingConversionsCheck::handleFloatingToBoolean( 311 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs, 312 const Expr &Rhs) { 313 return diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs); 314 } 315 316 void NarrowingConversionsCheck::handleBooleanToSignedIntegral( 317 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs, 318 const Expr &Rhs) { 319 // Conversion from Bool to SignedIntegral value is well defined. 320 321 // We keep this function (even if it is empty) to make sure that 322 // handleImplicitCast and handleBinaryOperator are symmetric in their behavior 323 // and handle the same cases. 324 } 325 326 void NarrowingConversionsCheck::handleFloatingCast(const ASTContext &Context, 327 SourceLocation SourceLoc, 328 const Expr &Lhs, 329 const Expr &Rhs) { 330 if (WarnOnFloatingPointNarrowingConversion) { 331 const BuiltinType *ToType = getBuiltinType(Lhs); 332 APValue Constant = getConstantExprValue(Context, Rhs); 333 if (Constant.isFloat()) { 334 // From [dcl.init.list]p7.2: 335 // Floating point constant narrowing only takes place when the value is 336 // not within destination range. We convert the value to the destination 337 // type and check if the resulting value is infinity. 338 llvm::APFloat Tmp = Constant.getFloat(); 339 bool UnusedLosesInfo; 340 Tmp.convert(Context.getFloatTypeSemantics(ToType->desugar()), 341 llvm::APFloatBase::rmNearestTiesToEven, &UnusedLosesInfo); 342 if (Tmp.isInfinity()) 343 diagNarrowConstant(SourceLoc, Lhs, Rhs); 344 return; 345 } 346 const BuiltinType *FromType = getBuiltinType(Rhs); 347 if (ToType->getKind() < FromType->getKind()) 348 diagNarrowType(SourceLoc, Lhs, Rhs); 349 } 350 } 351 352 void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context, 353 SourceLocation SourceLoc, 354 const Expr &Lhs, 355 const Expr &Rhs) { 356 assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() && 357 "Dependent types must be check before calling this function"); 358 const BuiltinType *LhsType = getBuiltinType(Lhs); 359 const BuiltinType *RhsType = getBuiltinType(Rhs); 360 if (RhsType == nullptr || LhsType == nullptr) 361 return; 362 if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger()) 363 return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs); 364 if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool) 365 return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs); 366 if (RhsType->isInteger() && LhsType->isFloatingPoint()) 367 return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs); 368 if (RhsType->isInteger() && LhsType->isInteger()) 369 return handleIntegralCast(Context, SourceLoc, Lhs, Rhs); 370 if (RhsType->isFloatingPoint() && LhsType->getKind() == BuiltinType::Bool) 371 return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs); 372 if (RhsType->isFloatingPoint() && LhsType->isInteger()) 373 return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs); 374 if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint()) 375 return handleFloatingCast(Context, SourceLoc, Lhs, Rhs); 376 } 377 378 bool NarrowingConversionsCheck::handleConditionalOperator( 379 const ASTContext &Context, const Expr &Lhs, const Expr &Rhs) { 380 if (const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) { 381 // We have an expression like so: `output = cond ? lhs : rhs` 382 // From the point of view of narrowing conversion we treat it as two 383 // expressions `output = lhs` and `output = rhs`. 384 handleBinaryOperator(Context, CO->getLHS()->getExprLoc(), Lhs, 385 *CO->getLHS()); 386 handleBinaryOperator(Context, CO->getRHS()->getExprLoc(), Lhs, 387 *CO->getRHS()); 388 return true; 389 } 390 return false; 391 } 392 393 void NarrowingConversionsCheck::handleImplicitCast( 394 const ASTContext &Context, const ImplicitCastExpr &Cast) { 395 if (Cast.getExprLoc().isMacroID()) 396 return; 397 const Expr &Lhs = Cast; 398 const Expr &Rhs = *Cast.getSubExpr(); 399 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent()) 400 return; 401 if (handleConditionalOperator(Context, Lhs, Rhs)) 402 return; 403 SourceLocation SourceLoc = Lhs.getExprLoc(); 404 switch (Cast.getCastKind()) { 405 case CK_BooleanToSignedIntegral: 406 return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs); 407 case CK_IntegralToBoolean: 408 return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs); 409 case CK_IntegralToFloating: 410 return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs); 411 case CK_IntegralCast: 412 return handleIntegralCast(Context, SourceLoc, Lhs, Rhs); 413 case CK_FloatingToBoolean: 414 return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs); 415 case CK_FloatingToIntegral: 416 return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs); 417 case CK_FloatingCast: 418 return handleFloatingCast(Context, SourceLoc, Lhs, Rhs); 419 default: 420 break; 421 } 422 } 423 424 void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context, 425 const BinaryOperator &Op) { 426 if (Op.getBeginLoc().isMacroID()) 427 return; 428 const Expr &Lhs = *Op.getLHS(); 429 const Expr &Rhs = *Op.getRHS(); 430 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent()) 431 return; 432 if (handleConditionalOperator(Context, Lhs, Rhs)) 433 return; 434 handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs); 435 } 436 437 void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) { 438 if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("binary_op")) 439 return handleBinaryOperator(*Result.Context, *Op); 440 if (const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast")) 441 return handleImplicitCast(*Result.Context, *Cast); 442 llvm_unreachable("must be binary operator or cast expression"); 443 } 444 445 } // namespace cppcoreguidelines 446 } // namespace tidy 447 } // namespace clang 448