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(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
77void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
78 Options.store(Options&: Opts, LocalName: "WarnOnSizeOfConstant", Value: WarnOnSizeOfConstant);
79 Options.store(Options&: Opts, LocalName: "WarnOnSizeOfIntegerExpression",
80 Value: WarnOnSizeOfIntegerExpression);
81 Options.store(Options&: Opts, LocalName: "WarnOnSizeOfThis", Value: WarnOnSizeOfThis);
82 Options.store(Options&: Opts, LocalName: "WarnOnSizeOfCompareToConstant",
83 Value: WarnOnSizeOfCompareToConstant);
84 Options.store(Options&: Opts, LocalName: "WarnOnSizeOfPointerToAggregate",
85 Value: WarnOnSizeOfPointerToAggregate);
86 Options.store(Options&: Opts, LocalName: "WarnOnSizeOfPointer", Value: WarnOnSizeOfPointer);
87 Options.store(Options&: Opts, LocalName: "WarnOnOffsetDividedBySizeOf",
88 Value: WarnOnOffsetDividedBySizeOf);
89}
90
91void 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(InnerMatcher: integerLiteral());
97 const auto ConstantExpr = ignoringParenImpCasts(
98 InnerMatcher: anyOf(integerLiteral(), unaryOperator(hasUnaryOperand(InnerMatcher: IntegerExpr)),
99 binaryOperator(hasLHS(InnerMatcher: IntegerExpr), hasRHS(InnerMatcher: IntegerExpr))));
100 const auto IntegerCallExpr = ignoringParenImpCasts(InnerMatcher: callExpr(
101 anyOf(hasType(InnerMatcher: isInteger()), hasType(InnerMatcher: hasCanonicalType(InnerMatcher: enumType()))),
102 unless(isInTemplateInstantiation())));
103 const auto SizeOfExpr = sizeOfExpr(InnerMatcher: hasArgumentOfType(
104 InnerMatcher: hasUnqualifiedDesugaredType(InnerMatcher: type().bind(ID: "sizeof-arg-type"))));
105 const auto SizeOfZero =
106 sizeOfExpr(InnerMatcher: has(ignoringParenImpCasts(InnerMatcher: integerLiteral(equals(Value: 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 NodeMatch: expr(sizeOfExpr(InnerMatcher: has(ignoringParenImpCasts(InnerMatcher: ConstantExpr))),
114 unless(SizeOfZero))
115 .bind(ID: "sizeof-constant"),
116 Action: this);
117 }
118
119 // Detect sizeof(f())
120 if (WarnOnSizeOfIntegerExpression) {
121 Finder->addMatcher(NodeMatch: sizeOfExpr(InnerMatcher: ignoringParenImpCasts(InnerMatcher: has(IntegerCallExpr)))
122 .bind(ID: "sizeof-integer-call"),
123 Action: this);
124 }
125
126 // Detect expression like: sizeof(this);
127 if (WarnOnSizeOfThis) {
128 Finder->addMatcher(NodeMatch: sizeOfExpr(InnerMatcher: has(ignoringParenImpCasts(InnerMatcher: cxxThisExpr())))
129 .bind(ID: "sizeof-this"),
130 Action: 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(InnerMatcher: hasCanonicalType(InnerMatcher: CharPtrType)),
137 hasInitializer(InnerMatcher: ignoringParenImpCasts(InnerMatcher: stringLiteral())));
138 const auto VarWithConstStrLiteralDecl = expr(
139 hasType(InnerMatcher: hasCanonicalType(InnerMatcher: CharPtrType)),
140 ignoringParenImpCasts(InnerMatcher: declRefExpr(hasDeclaration(InnerMatcher: ConstStrLiteralDecl))));
141 Finder->addMatcher(
142 NodeMatch: sizeOfExpr(InnerMatcher: has(ignoringParenImpCasts(InnerMatcher: VarWithConstStrLiteralDecl)))
143 .bind(ID: "sizeof-charp"),
144 Action: 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(InnerMatcher: hasType(InnerMatcher: hasCanonicalType(InnerMatcher: arrayType())));
164 const auto ArrayCastExpr = expr(anyOf(
165 unaryOperator(hasUnaryOperand(InnerMatcher: ArrayExpr), unless(hasOperatorName(Name: "*"))),
166 binaryOperator(hasEitherOperand(InnerMatcher: ArrayExpr)),
167 castExpr(hasSourceExpression(InnerMatcher: ArrayExpr))));
168 const auto PointerToArrayExpr =
169 hasType(InnerMatcher: hasCanonicalType(InnerMatcher: pointerType(pointee(arrayType()))));
170
171 const auto PointerToStructType =
172 hasUnqualifiedDesugaredType(InnerMatcher: pointerType(pointee(recordType())));
173 const auto PointerToStructExpr =
174 expr(hasType(InnerMatcher: hasCanonicalType(InnerMatcher: PointerToStructType)));
175
176 const auto PointerToDetectedExpr =
177 WarnOnSizeOfPointer
178 ? expr(hasType(InnerMatcher: hasUnqualifiedDesugaredType(InnerMatcher: pointerType())))
179 : expr(anyOf(ArrayCastExpr, PointerToArrayExpr,
180 PointerToStructExpr));
181
182 const auto ZeroLiteral = ignoringParenImpCasts(InnerMatcher: integerLiteral(equals(Value: 0)));
183 const auto SubscriptExprWithZeroIndex =
184 arraySubscriptExpr(hasIndex(InnerMatcher: ZeroLiteral));
185 const auto DerefExpr =
186 ignoringParenImpCasts(InnerMatcher: unaryOperator(hasOperatorName(Name: "*")));
187
188 Finder->addMatcher(
189 NodeMatch: expr(sizeOfExpr(InnerMatcher: has(ignoringParenImpCasts(InnerMatcher: expr(
190 PointerToDetectedExpr, unless(DerefExpr),
191 unless(SubscriptExprWithZeroIndex),
192 unless(VarWithConstStrLiteralDecl), unless(cxxThisExpr()))))))
193 .bind(ID: "sizeof-pointer"),
194 Action: this);
195 }
196
197 // Detect expression like: sizeof(expr) <= k for a suspicious constant 'k'.
198 if (WarnOnSizeOfCompareToConstant) {
199 Finder->addMatcher(
200 NodeMatch: binaryOperator(matchers::isRelationalOperator(),
201 hasOperands(Matcher1: ignoringParenImpCasts(InnerMatcher: SizeOfExpr),
202 Matcher2: ignoringParenImpCasts(InnerMatcher: integerLiteral(anyOf(
203 equals(Value: 0), isBiggerThan(N: 0x80000))))))
204 .bind(ID: "sizeof-compare-constant"),
205 Action: this);
206 }
207
208 // Detect expression like: sizeof(expr, expr); most likely an error.
209 Finder->addMatcher(
210 NodeMatch: sizeOfExpr(
211 InnerMatcher: has(ignoringParenImpCasts(
212 InnerMatcher: binaryOperator(hasOperatorName(Name: ",")).bind(ID: "sizeof-comma-binop"))))
213 .bind(ID: "sizeof-comma-expr"),
214 Action: this);
215
216 // Detect sizeof(...) /sizeof(...));
217 // FIXME:
218 // Re-evaluate what cases to handle by the checker.
219 // Probably any sizeof(A)/sizeof(B) should be error if
220 // 'A' is not an array (type) and 'B' the (type of the) first element of it.
221 // Except if 'A' and 'B' are non-pointers, then use the existing size division
222 // rule.
223 const auto ElemType =
224 arrayType(hasElementType(recordType().bind(ID: "elem-type")));
225 const auto ElemPtrType = pointerType(pointee(type().bind(ID: "elem-ptr-type")));
226 const auto SizeofDivideExpr = binaryOperator(
227 hasOperatorName(Name: "/"),
228 hasLHS(
229 InnerMatcher: ignoringParenImpCasts(InnerMatcher: sizeOfExpr(InnerMatcher: hasArgumentOfType(InnerMatcher: hasCanonicalType(
230 InnerMatcher: type(anyOf(ElemType, ElemPtrType, type())).bind(ID: "num-type")))))),
231 hasRHS(InnerMatcher: ignoringParenImpCasts(InnerMatcher: sizeOfExpr(
232 InnerMatcher: hasArgumentOfType(InnerMatcher: hasCanonicalType(InnerMatcher: type().bind(ID: "denom-type")))))));
233
234 Finder->addMatcher(NodeMatch: SizeofDivideExpr.bind(ID: "sizeof-divide-expr"), Action: this);
235
236 // Detect expression like: sizeof(...) * sizeof(...)); most likely an error.
237 Finder->addMatcher(NodeMatch: binaryOperator(hasOperatorName(Name: "*"),
238 hasLHS(InnerMatcher: ignoringParenImpCasts(InnerMatcher: SizeOfExpr)),
239 hasRHS(InnerMatcher: ignoringParenImpCasts(InnerMatcher: SizeOfExpr)))
240 .bind(ID: "sizeof-multiply-sizeof"),
241 Action: this);
242
243 Finder->addMatcher(
244 NodeMatch: binaryOperator(hasOperatorName(Name: "*"),
245 hasOperands(Matcher1: ignoringParenImpCasts(InnerMatcher: SizeOfExpr),
246 Matcher2: ignoringParenImpCasts(InnerMatcher: binaryOperator(
247 hasOperatorName(Name: "*"),
248 hasEitherOperand(
249 InnerMatcher: ignoringParenImpCasts(InnerMatcher: SizeOfExpr))))))
250 .bind(ID: "sizeof-multiply-sizeof"),
251 Action: this);
252
253 // Detect strange double-sizeof expression like: sizeof(sizeof(...));
254 // Note: The expression 'sizeof(sizeof(0))' is accepted.
255 Finder->addMatcher(NodeMatch: sizeOfExpr(InnerMatcher: has(ignoringParenImpCasts(InnerMatcher: hasSizeOfDescendant(
256 Depth: 8, InnerMatcher: allOf(SizeOfExpr, unless(SizeOfZero))))))
257 .bind(ID: "sizeof-sizeof-expr"),
258 Action: this);
259
260 // Detect sizeof usage in comparisons involving pointer arithmetics, such as
261 // N * sizeof(T) == P1 - P2 or (P1 - P2) / sizeof(T), where P1 and P2 are
262 // pointers to a type T.
263 const auto PtrDiffExpr = binaryOperator(
264 hasOperatorName(Name: "-"),
265 hasLHS(InnerMatcher: hasType(InnerMatcher: hasUnqualifiedDesugaredType(InnerMatcher: pointerType(pointee(
266 hasUnqualifiedDesugaredType(InnerMatcher: type().bind(ID: "left-ptr-type"))))))),
267 hasRHS(InnerMatcher: hasType(InnerMatcher: hasUnqualifiedDesugaredType(InnerMatcher: pointerType(pointee(
268 hasUnqualifiedDesugaredType(InnerMatcher: type().bind(ID: "right-ptr-type"))))))));
269
270 Finder->addMatcher(
271 NodeMatch: binaryOperator(
272 hasAnyOperatorName("==", "!=", "<", "<=", ">", ">=", "+", "-"),
273 hasOperands(Matcher1: anyOf(ignoringParenImpCasts(
274 InnerMatcher: SizeOfExpr.bind(ID: "sizeof-ptr-mul-expr")),
275 ignoringParenImpCasts(InnerMatcher: binaryOperator(
276 hasOperatorName(Name: "*"),
277 hasEitherOperand(InnerMatcher: ignoringParenImpCasts(
278 InnerMatcher: SizeOfExpr.bind(ID: "sizeof-ptr-mul-expr")))))),
279 Matcher2: ignoringParenImpCasts(InnerMatcher: PtrDiffExpr)))
280 .bind(ID: "sizeof-in-ptr-arithmetic-mul"),
281 Action: this);
282
283 Finder->addMatcher(
284 NodeMatch: binaryOperator(
285 hasOperatorName(Name: "/"), hasLHS(InnerMatcher: ignoringParenImpCasts(InnerMatcher: PtrDiffExpr)),
286 hasRHS(InnerMatcher: ignoringParenImpCasts(InnerMatcher: SizeOfExpr.bind(ID: "sizeof-ptr-div-expr"))))
287 .bind(ID: "sizeof-in-ptr-arithmetic-div"),
288 Action: this);
289
290 // SEI CERT ARR39-C. Do not add or subtract a scaled integer to a pointer.
291 // Detect sizeof, alignof and offsetof usage in pointer arithmetics where
292 // they are used to scale the numeric distance, which is scaled again by
293 // the pointer arithmetic operator. This can result in forming invalid
294 // offsets.
295 //
296 // Examples, where P is a pointer, N is some integer (both compile-time and
297 // run-time): P + sizeof(T), P + sizeof(*P), P + N * sizeof(*P).
298 //
299 // This check does not warn on cases where the pointee type is "1 byte",
300 // as those cases can often come from generics and also do not constitute a
301 // problem because the size does not affect the scale used.
302 const auto InterestingPtrTyForPtrArithmetic =
303 pointerType(pointee(qualType().bind(ID: "pointee-type")));
304 const auto SizeofLikeScaleExpr =
305 expr(anyOf(unaryExprOrTypeTraitExpr(ofKind(Kind: UETT_SizeOf)),
306 unaryExprOrTypeTraitExpr(ofKind(Kind: UETT_AlignOf)),
307 offsetOfExpr()))
308 .bind(ID: "sizeof-in-ptr-arithmetic-scale-expr");
309 const auto PtrArithmeticIntegerScaleExpr = binaryOperator(
310 WarnOnOffsetDividedBySizeOf ? binaryOperator(hasAnyOperatorName("*", "/"))
311 : binaryOperator(hasOperatorName(Name: "*")),
312 // sizeof(...) * sizeof(...) and sizeof(...) / sizeof(...) is handled
313 // by this check on another path.
314 hasOperands(Matcher1: expr(hasType(InnerMatcher: isInteger()), unless(SizeofLikeScaleExpr)),
315 Matcher2: SizeofLikeScaleExpr));
316 const auto PtrArithmeticScaledIntegerExpr =
317 expr(anyOf(SizeofLikeScaleExpr, PtrArithmeticIntegerScaleExpr),
318 unless(SizeofDivideExpr));
319
320 Finder->addMatcher(
321 NodeMatch: expr(anyOf(
322 binaryOperator(hasAnyOperatorName("+", "-"),
323 hasOperands(Matcher1: hasType(InnerMatcher: InterestingPtrTyForPtrArithmetic),
324 Matcher2: PtrArithmeticScaledIntegerExpr))
325 .bind(ID: "sizeof-in-ptr-arithmetic-plusminus"),
326 binaryOperator(hasAnyOperatorName("+=", "-="),
327 hasLHS(InnerMatcher: hasType(InnerMatcher: InterestingPtrTyForPtrArithmetic)),
328 hasRHS(InnerMatcher: PtrArithmeticScaledIntegerExpr))
329 .bind(ID: "sizeof-in-ptr-arithmetic-plusminus"))),
330 Action: this);
331}
332
333void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
334 const ASTContext &Ctx = *Result.Context;
335
336 if (const auto *E = Result.Nodes.getNodeAs<Expr>(ID: "sizeof-constant")) {
337 diag(E->getBeginLoc(), "suspicious usage of 'sizeof(K)'; did you mean 'K'?")
338 << E->getSourceRange();
339 } else if (const auto *E =
340 Result.Nodes.getNodeAs<Expr>(ID: "sizeof-integer-call")) {
341 diag(E->getBeginLoc(), "suspicious usage of 'sizeof()' on an expression "
342 "of integer type")
343 << E->getSourceRange();
344 } else if (const auto *E = Result.Nodes.getNodeAs<Expr>(ID: "sizeof-this")) {
345 diag(E->getBeginLoc(),
346 "suspicious usage of 'sizeof(this)'; did you mean 'sizeof(*this)'")
347 << E->getSourceRange();
348 } else if (const auto *E = Result.Nodes.getNodeAs<Expr>(ID: "sizeof-charp")) {
349 diag(E->getBeginLoc(),
350 "suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?")
351 << E->getSourceRange();
352 } else if (const auto *E = Result.Nodes.getNodeAs<Expr>(ID: "sizeof-pointer")) {
353 diag(E->getBeginLoc(), "suspicious usage of 'sizeof()' on an expression "
354 "of pointer type")
355 << E->getSourceRange();
356 } else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>(
357 ID: "sizeof-compare-constant")) {
358 diag(Loc: E->getOperatorLoc(),
359 Description: "suspicious comparison of 'sizeof(expr)' to a constant")
360 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
361 } else if (const auto *E =
362 Result.Nodes.getNodeAs<Expr>(ID: "sizeof-comma-expr")) {
363 const auto *BO =
364 Result.Nodes.getNodeAs<BinaryOperator>(ID: "sizeof-comma-binop");
365 assert(BO);
366 diag(Loc: BO->getOperatorLoc(), Description: "suspicious usage of 'sizeof(..., ...)'")
367 << E->getSourceRange();
368 } else if (const auto *E =
369 Result.Nodes.getNodeAs<BinaryOperator>(ID: "sizeof-divide-expr")) {
370 const auto *NumTy = Result.Nodes.getNodeAs<Type>(ID: "num-type");
371 const auto *DenomTy = Result.Nodes.getNodeAs<Type>(ID: "denom-type");
372 const auto *ElementTy = Result.Nodes.getNodeAs<Type>(ID: "elem-type");
373 const auto *PointedTy = Result.Nodes.getNodeAs<Type>(ID: "elem-ptr-type");
374
375 CharUnits NumeratorSize = getSizeOfType(Ctx, Ty: NumTy);
376 CharUnits DenominatorSize = getSizeOfType(Ctx, Ty: DenomTy);
377 CharUnits ElementSize = getSizeOfType(Ctx, Ty: ElementTy);
378
379 if (DenominatorSize > CharUnits::Zero() &&
380 !NumeratorSize.isMultipleOf(N: DenominatorSize)) {
381 diag(Loc: E->getOperatorLoc(), Description: "suspicious usage of 'sizeof(...)/sizeof(...)';"
382 " numerator is not a multiple of denominator")
383 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
384 } else if (ElementSize > CharUnits::Zero() &&
385 DenominatorSize > CharUnits::Zero() &&
386 ElementSize != DenominatorSize) {
387 // FIXME: Apparently there are no testcases that cover this branch!
388 diag(Loc: E->getOperatorLoc(),
389 Description: "suspicious usage of 'sizeof(array)/sizeof(...)';"
390 " denominator differs from the size of array elements")
391 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
392 } else if (NumTy && DenomTy && NumTy == DenomTy &&
393 !NumTy->isDependentType()) {
394 // Dependent type should not be compared.
395 diag(Loc: E->getOperatorLoc(),
396 Description: "suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions "
397 "have the same type")
398 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
399 } else if (!WarnOnSizeOfPointer) {
400 // When 'WarnOnSizeOfPointer' is enabled, these messages become redundant:
401 if (PointedTy && DenomTy && PointedTy == DenomTy) {
402 diag(Loc: E->getOperatorLoc(),
403 Description: "suspicious usage of 'sizeof(...)/sizeof(...)'; size of pointer "
404 "is divided by size of pointed type")
405 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
406 } else if (NumTy && DenomTy && NumTy->isPointerType() &&
407 DenomTy->isPointerType()) {
408 diag(Loc: E->getOperatorLoc(),
409 Description: "suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions "
410 "have pointer types")
411 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
412 }
413 }
414 } else if (const auto *E =
415 Result.Nodes.getNodeAs<Expr>(ID: "sizeof-sizeof-expr")) {
416 diag(E->getBeginLoc(), "suspicious usage of 'sizeof(sizeof(...))'")
417 << E->getSourceRange();
418 } else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>(
419 ID: "sizeof-multiply-sizeof")) {
420 diag(Loc: E->getOperatorLoc(), Description: "suspicious 'sizeof' by 'sizeof' multiplication")
421 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
422 } else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>(
423 ID: "sizeof-in-ptr-arithmetic-mul")) {
424 const auto *LPtrTy = Result.Nodes.getNodeAs<Type>(ID: "left-ptr-type");
425 const auto *RPtrTy = Result.Nodes.getNodeAs<Type>(ID: "right-ptr-type");
426 const auto *SizeofArgTy = Result.Nodes.getNodeAs<Type>(ID: "sizeof-arg-type");
427 const auto *SizeOfExpr =
428 Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>(ID: "sizeof-ptr-mul-expr");
429
430 if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) {
431 diag(Loc: SizeOfExpr->getBeginLoc(), Description: "suspicious usage of 'sizeof(...)' in "
432 "pointer arithmetic")
433 << SizeOfExpr->getSourceRange() << E->getOperatorLoc()
434 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
435 }
436 } else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>(
437 ID: "sizeof-in-ptr-arithmetic-div")) {
438 const auto *LPtrTy = Result.Nodes.getNodeAs<Type>(ID: "left-ptr-type");
439 const auto *RPtrTy = Result.Nodes.getNodeAs<Type>(ID: "right-ptr-type");
440 const auto *SizeofArgTy = Result.Nodes.getNodeAs<Type>(ID: "sizeof-arg-type");
441 const auto *SizeOfExpr =
442 Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>(ID: "sizeof-ptr-div-expr");
443
444 if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) {
445 diag(Loc: SizeOfExpr->getBeginLoc(), Description: "suspicious usage of 'sizeof(...)' in "
446 "pointer arithmetic")
447 << SizeOfExpr->getSourceRange() << E->getOperatorLoc()
448 << E->getLHS()->getSourceRange() << E->getRHS()->getSourceRange();
449 }
450 } else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>(
451 ID: "sizeof-in-ptr-arithmetic-plusminus")) {
452 const auto *PointeeTy = Result.Nodes.getNodeAs<QualType>(ID: "pointee-type");
453 const auto *ScaleExpr =
454 Result.Nodes.getNodeAs<Expr>(ID: "sizeof-in-ptr-arithmetic-scale-expr");
455 const CharUnits PointeeSize = getSizeOfType(Ctx, Ty: PointeeTy->getTypePtr());
456 const int ScaleKind = [ScaleExpr]() {
457 if (const auto *UTTE = dyn_cast<UnaryExprOrTypeTraitExpr>(Val: ScaleExpr))
458 switch (UTTE->getKind()) {
459 case UETT_SizeOf:
460 return 0;
461 case UETT_AlignOf:
462 return 1;
463 default:
464 return -1;
465 }
466
467 if (isa<OffsetOfExpr>(Val: ScaleExpr))
468 return 2;
469
470 return -1;
471 }();
472
473 if (ScaleKind != -1 && PointeeSize > CharUnits::One()) {
474 diag(Loc: E->getExprLoc(),
475 Description: "suspicious usage of '%select{sizeof|alignof|offsetof}0(...)' in "
476 "pointer arithmetic; this scaled value will be scaled again by the "
477 "'%1' operator")
478 << ScaleKind << E->getOpcodeStr() << ScaleExpr->getSourceRange();
479 diag(Loc: E->getExprLoc(),
480 Description: "'%0' in pointer arithmetic internally scales with 'sizeof(%1)' == "
481 "%2",
482 Level: DiagnosticIDs::Note)
483 << E->getOpcodeStr()
484 << PointeeTy->getAsString(Policy: Ctx.getPrintingPolicy())
485 << PointeeSize.getQuantity();
486 }
487 }
488}
489
490} // namespace clang::tidy::bugprone
491

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

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