[TargetVersion] Only enable on RISC-V and AArch64 (#115991)
[llvm-project.git] / clang-tools-extra / clang-tidy / bugprone / SizeofExpressionCheck.cpp
blobf3d4c2255d86ec5eaa0dc1f9a226dc41372a0f2a
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 //===----------------------------------------------------------------------===//
9 #include "SizeofExpressionCheck.h"
10 #include "../utils/Matchers.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 using namespace clang::ast_matchers;
16 namespace clang::tidy::bugprone {
18 namespace {
20 AST_MATCHER_P(IntegerLiteral, isBiggerThan, unsigned, N) {
21 return Node.getValue().ugt(N);
24 AST_MATCHER_P2(Expr, hasSizeOfDescendant, int, Depth,
25 ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
26 if (Depth < 0)
27 return false;
29 const Expr *E = Node.IgnoreParenImpCasts();
30 if (InnerMatcher.matches(*E, Finder, Builder))
31 return true;
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);
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);
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);
48 return false;
51 AST_MATCHER(Expr, offsetOfExpr) { return isa<OffsetOfExpr>(Node); }
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);
60 } // namespace
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)) {}
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);
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.
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)))));
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);
119 // Detect sizeof(f())
120 if (WarnOnSizeOfIntegerExpression) {
121 Finder->addMatcher(sizeOfExpr(ignoringParenImpCasts(has(IntegerCallExpr)))
122 .bind("sizeof-integer-call"),
123 this);
126 // Detect expression like: sizeof(this);
127 if (WarnOnSizeOfThis) {
128 Finder->addMatcher(sizeOfExpr(has(ignoringParenImpCasts(cxxThisExpr())))
129 .bind("sizeof-this"),
130 this);
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);
146 // Detect sizeof(ptr) where ptr is a pointer (CWE-467).
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.)
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));`.)
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()))));
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)));
178 const auto PointerToDetectedExpr =
179 WarnOnSizeOfPointer
180 ? expr(hasType(hasUnqualifiedDesugaredType(pointerType())))
181 : expr(anyOf(ArrayCastExpr, PointerToArrayExpr,
182 PointerToStructExpr));
184 const auto ZeroLiteral = ignoringParenImpCasts(integerLiteral(equals(0)));
185 const auto SubscriptExprWithZeroIndex =
186 arraySubscriptExpr(hasIndex(ZeroLiteral));
187 const auto DerefExpr =
188 ignoringParenImpCasts(unaryOperator(hasOperatorName("*")));
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);
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);
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);
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")))))));
238 Finder->addMatcher(SizeofDivideExpr.bind("sizeof-divide-expr"), this);
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);
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);
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);
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"))))))));
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);
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);
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.
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).
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));
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);
337 void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
338 const ASTContext &Ctx = *Result.Context;
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();
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");
386 CharUnits NumeratorSize = getSizeOfType(Ctx, NumTy);
387 CharUnits DenominatorSize = getSizeOfType(Ctx, DenomTy);
388 CharUnits ElementSize = getSizeOfType(Ctx, ElementTy);
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();
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");
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();
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");
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();
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;
478 if (isa<OffsetOfExpr>(ScaleExpr))
479 return 2;
481 return -1;
482 }();
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();
501 } // namespace clang::tidy::bugprone