xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp (revision e855feac41fd89aebf540a155d21f12a3e82f05b)
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