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

source code of clang-tools-extra/clang-tidy/bugprone/SizeofExpressionCheck.cpp