1 //===--- SizeofExpressionCheck.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 "SizeofExpressionCheck.h" 10 #include "../utils/Matchers.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 14 using namespace clang::ast_matchers; 15 16 namespace clang::tidy::bugprone { 17 18 namespace { 19 20 AST_MATCHER_P(IntegerLiteral, isBiggerThan, unsigned, N) { 21 return Node.getValue().ugt(N); 22 } 23 24 AST_MATCHER_P2(Expr, hasSizeOfDescendant, int, Depth, 25 ast_matchers::internal::Matcher<Expr>, InnerMatcher) { 26 if (Depth < 0) 27 return false; 28 29 const Expr *E = Node.IgnoreParenImpCasts(); 30 if (InnerMatcher.matches(*E, Finder, Builder)) 31 return true; 32 33 if (const auto *CE = dyn_cast<CastExpr>(E)) { 34 const auto M = hasSizeOfDescendant(Depth - 1, InnerMatcher); 35 return M.matches(*CE->getSubExpr(), Finder, Builder); 36 } 37 if (const auto *UE = dyn_cast<UnaryOperator>(E)) { 38 const auto M = hasSizeOfDescendant(Depth - 1, InnerMatcher); 39 return M.matches(*UE->getSubExpr(), Finder, Builder); 40 } 41 if (const auto *BE = dyn_cast<BinaryOperator>(E)) { 42 const auto LHS = hasSizeOfDescendant(Depth - 1, InnerMatcher); 43 const auto RHS = hasSizeOfDescendant(Depth - 1, InnerMatcher); 44 return LHS.matches(*BE->getLHS(), Finder, Builder) || 45 RHS.matches(*BE->getRHS(), Finder, Builder); 46 } 47 48 return false; 49 } 50 51 AST_MATCHER(Expr, offsetOfExpr) { return isa<OffsetOfExpr>(Node); } 52 53 CharUnits getSizeOfType(const ASTContext &Ctx, const Type *Ty) { 54 if (!Ty || Ty->isIncompleteType() || Ty->isDependentType() || 55 isa<DependentSizedArrayType>(Ty) || !Ty->isConstantSizeType()) 56 return CharUnits::Zero(); 57 return Ctx.getTypeSizeInChars(Ty); 58 } 59 60 } // namespace 61 62 SizeofExpressionCheck::SizeofExpressionCheck(StringRef Name, 63 ClangTidyContext *Context) 64 : ClangTidyCheck(Name, Context), 65 WarnOnSizeOfConstant(Options.get("WarnOnSizeOfConstant", true)), 66 WarnOnSizeOfIntegerExpression( 67 Options.get("WarnOnSizeOfIntegerExpression", false)), 68 WarnOnSizeOfThis(Options.get("WarnOnSizeOfThis", true)), 69 WarnOnSizeOfCompareToConstant( 70 Options.get("WarnOnSizeOfCompareToConstant", true)), 71 WarnOnSizeOfPointerToAggregate( 72 Options.get("WarnOnSizeOfPointerToAggregate", true)), 73 WarnOnSizeOfPointer(Options.get("WarnOnSizeOfPointer", false)), 74 WarnOnOffsetDividedBySizeOf( 75 Options.get("WarnOnOffsetDividedBySizeOf", true)) {} 76 77 void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 78 Options.store(Opts, "WarnOnSizeOfConstant", WarnOnSizeOfConstant); 79 Options.store(Opts, "WarnOnSizeOfIntegerExpression", 80 WarnOnSizeOfIntegerExpression); 81 Options.store(Opts, "WarnOnSizeOfThis", WarnOnSizeOfThis); 82 Options.store(Opts, "WarnOnSizeOfCompareToConstant", 83 WarnOnSizeOfCompareToConstant); 84 Options.store(Opts, "WarnOnSizeOfPointerToAggregate", 85 WarnOnSizeOfPointerToAggregate); 86 Options.store(Opts, "WarnOnSizeOfPointer", WarnOnSizeOfPointer); 87 Options.store(Opts, "WarnOnOffsetDividedBySizeOf", 88 WarnOnOffsetDividedBySizeOf); 89 } 90 91 void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) { 92 // FIXME: 93 // Some of the checks should not match in template code to avoid false 94 // positives if sizeof is applied on template argument. 95 96 const auto IntegerExpr = ignoringParenImpCasts(integerLiteral()); 97 const auto ConstantExpr = ignoringParenImpCasts( 98 anyOf(integerLiteral(), unaryOperator(hasUnaryOperand(IntegerExpr)), 99 binaryOperator(hasLHS(IntegerExpr), hasRHS(IntegerExpr)))); 100 const auto IntegerCallExpr = ignoringParenImpCasts(callExpr( 101 anyOf(hasType(isInteger()), hasType(hasCanonicalType(enumType()))), 102 unless(isInTemplateInstantiation()))); 103 const auto SizeOfExpr = sizeOfExpr(hasArgumentOfType( 104 hasUnqualifiedDesugaredType(type().bind("sizeof-arg-type")))); 105 const auto SizeOfZero = 106 sizeOfExpr(has(ignoringParenImpCasts(integerLiteral(equals(0))))); 107 108 // Detect expression like: sizeof(ARRAYLEN); 109 // Note: The expression 'sizeof(sizeof(0))' is a portable trick used to know 110 // the sizeof size_t. 111 if (WarnOnSizeOfConstant) { 112 Finder->addMatcher( 113 expr(sizeOfExpr(has(ignoringParenImpCasts(ConstantExpr))), 114 unless(SizeOfZero)) 115 .bind("sizeof-constant"), 116 this); 117 } 118 119 // Detect sizeof(f()) 120 if (WarnOnSizeOfIntegerExpression) { 121 Finder->addMatcher(sizeOfExpr(ignoringParenImpCasts(has(IntegerCallExpr))) 122 .bind("sizeof-integer-call"), 123 this); 124 } 125 126 // Detect expression like: sizeof(this); 127 if (WarnOnSizeOfThis) { 128 Finder->addMatcher(sizeOfExpr(has(ignoringParenImpCasts(cxxThisExpr()))) 129 .bind("sizeof-this"), 130 this); 131 } 132 133 // Detect sizeof(kPtr) where kPtr is 'const char* kPtr = "abc"'; 134 const auto CharPtrType = pointerType(pointee(isAnyCharacter())); 135 const auto ConstStrLiteralDecl = 136 varDecl(isDefinition(), hasType(hasCanonicalType(CharPtrType)), 137 hasInitializer(ignoringParenImpCasts(stringLiteral()))); 138 const auto VarWithConstStrLiteralDecl = expr( 139 hasType(hasCanonicalType(CharPtrType)), 140 ignoringParenImpCasts(declRefExpr(hasDeclaration(ConstStrLiteralDecl)))); 141 Finder->addMatcher( 142 sizeOfExpr(has(ignoringParenImpCasts(VarWithConstStrLiteralDecl))) 143 .bind("sizeof-charp"), 144 this); 145 146 // Detect sizeof(ptr) where ptr is a pointer (CWE-467). 147 // 148 // In WarnOnSizeOfPointerToAggregate mode only report cases when ptr points 149 // to an aggregate type or ptr is an expression that (implicitly or 150 // explicitly) casts an array to a pointer type. (These are more suspicious 151 // than other sizeof(ptr) expressions because they can appear as distorted 152 // forms of the common sizeof(aggregate) expressions.) 153 // 154 // To avoid false positives, the check doesn't report expressions like 155 // 'sizeof(pp[0])' and 'sizeof(*pp)' where `pp` is a pointer-to-pointer or 156 // array of pointers. (This filters out both `sizeof(arr) / sizeof(arr[0])` 157 // expressions and other cases like `p = realloc(p, newsize * sizeof(*p));`.) 158 // 159 // Moreover this generic message is suppressed in cases that are also matched 160 // by the more concrete matchers 'sizeof-this' and 'sizeof-charp'. 161 if (WarnOnSizeOfPointerToAggregate || WarnOnSizeOfPointer) { 162 const auto ArrayExpr = 163 ignoringParenImpCasts(hasType(hasCanonicalType(arrayType()))); 164 const auto ArrayCastExpr = expr(anyOf( 165 unaryOperator(hasUnaryOperand(ArrayExpr), unless(hasOperatorName("*"))), 166 binaryOperator(hasEitherOperand(ArrayExpr)), 167 castExpr(hasSourceExpression(ArrayExpr)))); 168 const auto PointerToArrayExpr = 169 hasType(hasCanonicalType(pointerType(pointee(arrayType())))); 170 171 const auto PointerToStructType = 172 hasUnqualifiedDesugaredType(pointerType(pointee(recordType()))); 173 const auto PointerToStructTypeWithBinding = 174 type(PointerToStructType).bind("struct-type"); 175 const auto PointerToStructExpr = 176 expr(hasType(hasCanonicalType(PointerToStructType))); 177 178 const auto PointerToDetectedExpr = 179 WarnOnSizeOfPointer 180 ? expr(hasType(hasUnqualifiedDesugaredType(pointerType()))) 181 : expr(anyOf(ArrayCastExpr, PointerToArrayExpr, 182 PointerToStructExpr)); 183 184 const auto ZeroLiteral = ignoringParenImpCasts(integerLiteral(equals(0))); 185 const auto SubscriptExprWithZeroIndex = 186 arraySubscriptExpr(hasIndex(ZeroLiteral)); 187 const auto DerefExpr = 188 ignoringParenImpCasts(unaryOperator(hasOperatorName("*"))); 189 190 Finder->addMatcher( 191 expr(sizeOfExpr(anyOf(has(ignoringParenImpCasts( 192 expr(PointerToDetectedExpr, unless(DerefExpr), 193 unless(SubscriptExprWithZeroIndex), 194 unless(VarWithConstStrLiteralDecl), 195 unless(cxxThisExpr())))), 196 has(PointerToStructTypeWithBinding)))) 197 .bind("sizeof-pointer"), 198 this); 199 } 200 201 // Detect expression like: sizeof(expr) <= k for a suspicious constant 'k'. 202 if (WarnOnSizeOfCompareToConstant) { 203 Finder->addMatcher( 204 binaryOperator(matchers::isRelationalOperator(), 205 hasOperands(ignoringParenImpCasts(SizeOfExpr), 206 ignoringParenImpCasts(integerLiteral(anyOf( 207 equals(0), isBiggerThan(0x80000)))))) 208 .bind("sizeof-compare-constant"), 209 this); 210 } 211 212 // Detect expression like: sizeof(expr, expr); most likely an error. 213 Finder->addMatcher( 214 sizeOfExpr( 215 has(ignoringParenImpCasts( 216 binaryOperator(hasOperatorName(",")).bind("sizeof-comma-binop")))) 217 .bind("sizeof-comma-expr"), 218 this); 219 220 // Detect sizeof(...) /sizeof(...)); 221 // FIXME: 222 // Re-evaluate what cases to handle by the checker. 223 // Probably any sizeof(A)/sizeof(B) should be error if 224 // 'A' is not an array (type) and 'B' the (type of the) first element of it. 225 // Except if 'A' and 'B' are non-pointers, then use the existing size division 226 // rule. 227 const auto ElemType = 228 arrayType(hasElementType(recordType().bind("elem-type"))); 229 const auto ElemPtrType = pointerType(pointee(type().bind("elem-ptr-type"))); 230 const auto SizeofDivideExpr = binaryOperator( 231 hasOperatorName("/"), 232 hasLHS( 233 ignoringParenImpCasts(sizeOfExpr(hasArgumentOfType(hasCanonicalType( 234 type(anyOf(ElemType, ElemPtrType, type())).bind("num-type")))))), 235 hasRHS(ignoringParenImpCasts(sizeOfExpr( 236 hasArgumentOfType(hasCanonicalType(type().bind("denom-type"))))))); 237 238 Finder->addMatcher(SizeofDivideExpr.bind("sizeof-divide-expr"), this); 239 240 // Detect expression like: sizeof(...) * sizeof(...)); most likely an error. 241 Finder->addMatcher(binaryOperator(hasOperatorName("*"), 242 hasLHS(ignoringParenImpCasts(SizeOfExpr)), 243 hasRHS(ignoringParenImpCasts(SizeOfExpr))) 244 .bind("sizeof-multiply-sizeof"), 245 this); 246 247 Finder->addMatcher( 248 binaryOperator(hasOperatorName("*"), 249 hasOperands(ignoringParenImpCasts(SizeOfExpr), 250 ignoringParenImpCasts(binaryOperator( 251 hasOperatorName("*"), 252 hasEitherOperand( 253 ignoringParenImpCasts(SizeOfExpr)))))) 254 .bind("sizeof-multiply-sizeof"), 255 this); 256 257 // Detect strange double-sizeof expression like: sizeof(sizeof(...)); 258 // Note: The expression 'sizeof(sizeof(0))' is accepted. 259 Finder->addMatcher(sizeOfExpr(has(ignoringParenImpCasts(hasSizeOfDescendant( 260 8, allOf(SizeOfExpr, unless(SizeOfZero)))))) 261 .bind("sizeof-sizeof-expr"), 262 this); 263 264 // Detect sizeof usage in comparisons involving pointer arithmetics, such as 265 // N * sizeof(T) == P1 - P2 or (P1 - P2) / sizeof(T), where P1 and P2 are 266 // pointers to a type T. 267 const auto PtrDiffExpr = binaryOperator( 268 hasOperatorName("-"), 269 hasLHS(hasType(hasUnqualifiedDesugaredType(pointerType(pointee( 270 hasUnqualifiedDesugaredType(type().bind("left-ptr-type"))))))), 271 hasRHS(hasType(hasUnqualifiedDesugaredType(pointerType(pointee( 272 hasUnqualifiedDesugaredType(type().bind("right-ptr-type")))))))); 273 274 Finder->addMatcher( 275 binaryOperator( 276 hasAnyOperatorName("==", "!=", "<", "<=", ">", ">=", "+", "-"), 277 hasOperands(anyOf(ignoringParenImpCasts( 278 SizeOfExpr.bind("sizeof-ptr-mul-expr")), 279 ignoringParenImpCasts(binaryOperator( 280 hasOperatorName("*"), 281 hasEitherOperand(ignoringParenImpCasts( 282 SizeOfExpr.bind("sizeof-ptr-mul-expr")))))), 283 ignoringParenImpCasts(PtrDiffExpr))) 284 .bind("sizeof-in-ptr-arithmetic-mul"), 285 this); 286 287 Finder->addMatcher( 288 binaryOperator( 289 hasOperatorName("/"), hasLHS(ignoringParenImpCasts(PtrDiffExpr)), 290 hasRHS(ignoringParenImpCasts(SizeOfExpr.bind("sizeof-ptr-div-expr")))) 291 .bind("sizeof-in-ptr-arithmetic-div"), 292 this); 293 294 // SEI CERT ARR39-C. Do not add or subtract a scaled integer to a pointer. 295 // Detect sizeof, alignof and offsetof usage in pointer arithmetics where 296 // they are used to scale the numeric distance, which is scaled again by 297 // the pointer arithmetic operator. This can result in forming invalid 298 // offsets. 299 // 300 // Examples, where P is a pointer, N is some integer (both compile-time and 301 // run-time): P + sizeof(T), P + sizeof(*P), P + N * sizeof(*P). 302 // 303 // This check does not warn on cases where the pointee type is "1 byte", 304 // as those cases can often come from generics and also do not constitute a 305 // problem because the size does not affect the scale used. 306 const auto InterestingPtrTyForPtrArithmetic = 307 pointerType(pointee(qualType().bind("pointee-type"))); 308 const auto SizeofLikeScaleExpr = 309 expr(anyOf(unaryExprOrTypeTraitExpr(ofKind(UETT_SizeOf)), 310 unaryExprOrTypeTraitExpr(ofKind(UETT_AlignOf)), 311 offsetOfExpr())) 312 .bind("sizeof-in-ptr-arithmetic-scale-expr"); 313 const auto PtrArithmeticIntegerScaleExpr = binaryOperator( 314 WarnOnOffsetDividedBySizeOf ? binaryOperator(hasAnyOperatorName("*", "/")) 315 : binaryOperator(hasOperatorName("*")), 316 // sizeof(...) * sizeof(...) and sizeof(...) / sizeof(...) is handled 317 // by this check on another path. 318 hasOperands(expr(hasType(isInteger()), unless(SizeofLikeScaleExpr)), 319 SizeofLikeScaleExpr)); 320 const auto PtrArithmeticScaledIntegerExpr = 321 expr(anyOf(SizeofLikeScaleExpr, PtrArithmeticIntegerScaleExpr), 322 unless(SizeofDivideExpr)); 323 324 Finder->addMatcher( 325 expr(anyOf( 326 binaryOperator(hasAnyOperatorName("+", "-"), 327 hasOperands(hasType(InterestingPtrTyForPtrArithmetic), 328 PtrArithmeticScaledIntegerExpr)) 329 .bind("sizeof-in-ptr-arithmetic-plusminus"), 330 binaryOperator(hasAnyOperatorName("+=", "-="), 331 hasLHS(hasType(InterestingPtrTyForPtrArithmetic)), 332 hasRHS(PtrArithmeticScaledIntegerExpr)) 333 .bind("sizeof-in-ptr-arithmetic-plusminus"))), 334 this); 335 } 336 337 void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) { 338 const ASTContext &Ctx = *Result.Context; 339 340 if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-constant")) { 341 diag(E->getBeginLoc(), "suspicious usage of 'sizeof(K)'; did you mean 'K'?") 342 << E->getSourceRange(); 343 } else if (const auto *E = 344 Result.Nodes.getNodeAs<Expr>("sizeof-integer-call")) { 345 diag(E->getBeginLoc(), "suspicious usage of 'sizeof()' on an expression " 346 "of integer type") 347 << E->getSourceRange(); 348 } else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-this")) { 349 diag(E->getBeginLoc(), 350 "suspicious usage of 'sizeof(this)'; did you mean 'sizeof(*this)'") 351 << E->getSourceRange(); 352 } else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-charp")) { 353 diag(E->getBeginLoc(), 354 "suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?") 355 << E->getSourceRange(); 356 } else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-pointer")) { 357 if (Result.Nodes.getNodeAs<Type>("struct-type")) { 358 diag(E->getBeginLoc(), 359 "suspicious usage of 'sizeof(A*)' on pointer-to-aggregate type; did " 360 "you mean 'sizeof(A)'?") 361 << E->getSourceRange(); 362 } else { 363 diag(E->getBeginLoc(), "suspicious usage of 'sizeof()' on an expression " 364 "of pointer type") 365 << E->getSourceRange(); 366 } 367 } else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>( 368 "sizeof-compare-constant")) { 369 diag(E->getOperatorLoc(), 370 "suspicious comparison of 'sizeof(expr)' to a constant") 371 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); 372 } else if (const auto *E = 373 Result.Nodes.getNodeAs<Expr>("sizeof-comma-expr")) { 374 const auto *BO = 375 Result.Nodes.getNodeAs<BinaryOperator>("sizeof-comma-binop"); 376 assert(BO); 377 diag(BO->getOperatorLoc(), "suspicious usage of 'sizeof(..., ...)'") 378 << E->getSourceRange(); 379 } else if (const auto *E = 380 Result.Nodes.getNodeAs<BinaryOperator>("sizeof-divide-expr")) { 381 const auto *NumTy = Result.Nodes.getNodeAs<Type>("num-type"); 382 const auto *DenomTy = Result.Nodes.getNodeAs<Type>("denom-type"); 383 const auto *ElementTy = Result.Nodes.getNodeAs<Type>("elem-type"); 384 const auto *PointedTy = Result.Nodes.getNodeAs<Type>("elem-ptr-type"); 385 386 CharUnits NumeratorSize = getSizeOfType(Ctx, NumTy); 387 CharUnits DenominatorSize = getSizeOfType(Ctx, DenomTy); 388 CharUnits ElementSize = getSizeOfType(Ctx, ElementTy); 389 390 if (DenominatorSize > CharUnits::Zero() && 391 !NumeratorSize.isMultipleOf(DenominatorSize)) { 392 diag(E->getOperatorLoc(), "suspicious usage of 'sizeof(...)/sizeof(...)';" 393 " numerator is not a multiple of denominator") 394 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); 395 } else if (ElementSize > CharUnits::Zero() && 396 DenominatorSize > CharUnits::Zero() && 397 ElementSize != DenominatorSize) { 398 // FIXME: Apparently there are no testcases that cover this branch! 399 diag(E->getOperatorLoc(), 400 "suspicious usage of 'sizeof(array)/sizeof(...)';" 401 " denominator differs from the size of array elements") 402 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); 403 } else if (NumTy && DenomTy && NumTy == DenomTy && 404 !NumTy->isDependentType()) { 405 // Dependent type should not be compared. 406 diag(E->getOperatorLoc(), 407 "suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions " 408 "have the same type") 409 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); 410 } else if (!WarnOnSizeOfPointer) { 411 // When 'WarnOnSizeOfPointer' is enabled, these messages become redundant: 412 if (PointedTy && DenomTy && PointedTy == DenomTy) { 413 diag(E->getOperatorLoc(), 414 "suspicious usage of 'sizeof(...)/sizeof(...)'; size of pointer " 415 "is divided by size of pointed type") 416 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); 417 } else if (NumTy && DenomTy && NumTy->isPointerType() && 418 DenomTy->isPointerType()) { 419 diag(E->getOperatorLoc(), 420 "suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions " 421 "have pointer types") 422 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); 423 } 424 } 425 } else if (const auto *E = 426 Result.Nodes.getNodeAs<Expr>("sizeof-sizeof-expr")) { 427 diag(E->getBeginLoc(), "suspicious usage of 'sizeof(sizeof(...))'") 428 << E->getSourceRange(); 429 } else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>( 430 "sizeof-multiply-sizeof")) { 431 diag(E->getOperatorLoc(), "suspicious 'sizeof' by 'sizeof' multiplication") 432 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); 433 } else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>( 434 "sizeof-in-ptr-arithmetic-mul")) { 435 const auto *LPtrTy = Result.Nodes.getNodeAs<Type>("left-ptr-type"); 436 const auto *RPtrTy = Result.Nodes.getNodeAs<Type>("right-ptr-type"); 437 const auto *SizeofArgTy = Result.Nodes.getNodeAs<Type>("sizeof-arg-type"); 438 const auto *SizeOfExpr = 439 Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>("sizeof-ptr-mul-expr"); 440 441 if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) { 442 diag(SizeOfExpr->getBeginLoc(), "suspicious usage of 'sizeof(...)' in " 443 "pointer arithmetic") 444 << SizeOfExpr->getSourceRange() << E->getOperatorLoc() 445 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); 446 } 447 } else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>( 448 "sizeof-in-ptr-arithmetic-div")) { 449 const auto *LPtrTy = Result.Nodes.getNodeAs<Type>("left-ptr-type"); 450 const auto *RPtrTy = Result.Nodes.getNodeAs<Type>("right-ptr-type"); 451 const auto *SizeofArgTy = Result.Nodes.getNodeAs<Type>("sizeof-arg-type"); 452 const auto *SizeOfExpr = 453 Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>("sizeof-ptr-div-expr"); 454 455 if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) { 456 diag(SizeOfExpr->getBeginLoc(), "suspicious usage of 'sizeof(...)' in " 457 "pointer arithmetic") 458 << SizeOfExpr->getSourceRange() << E->getOperatorLoc() 459 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange(); 460 } 461 } else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>( 462 "sizeof-in-ptr-arithmetic-plusminus")) { 463 const auto *PointeeTy = Result.Nodes.getNodeAs<QualType>("pointee-type"); 464 const auto *ScaleExpr = 465 Result.Nodes.getNodeAs<Expr>("sizeof-in-ptr-arithmetic-scale-expr"); 466 const CharUnits PointeeSize = getSizeOfType(Ctx, PointeeTy->getTypePtr()); 467 const int ScaleKind = [ScaleExpr]() { 468 if (const auto *UTTE = dyn_cast<UnaryExprOrTypeTraitExpr>(ScaleExpr)) 469 switch (UTTE->getKind()) { 470 case UETT_SizeOf: 471 return 0; 472 case UETT_AlignOf: 473 return 1; 474 default: 475 return -1; 476 } 477 478 if (isa<OffsetOfExpr>(ScaleExpr)) 479 return 2; 480 481 return -1; 482 }(); 483 484 if (ScaleKind != -1 && PointeeSize > CharUnits::One()) { 485 diag(E->getExprLoc(), 486 "suspicious usage of '%select{sizeof|alignof|offsetof}0(...)' in " 487 "pointer arithmetic; this scaled value will be scaled again by the " 488 "'%1' operator") 489 << ScaleKind << E->getOpcodeStr() << ScaleExpr->getSourceRange(); 490 diag(E->getExprLoc(), 491 "'%0' in pointer arithmetic internally scales with 'sizeof(%1)' == " 492 "%2", 493 DiagnosticIDs::Note) 494 << E->getOpcodeStr() 495 << PointeeTy->getAsString(Ctx.getPrintingPolicy()) 496 << PointeeSize.getQuantity(); 497 } 498 } 499 } 500 501 } // namespace clang::tidy::bugprone 502