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(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 | |
51 | AST_MATCHER(Expr, offsetOfExpr) { return isa<OffsetOfExpr>(Val: Node); } |
52 | |
53 | CharUnits 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 | |
62 | SizeofExpressionCheck::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 | |
77 | void 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 | |
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(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 | |
333 | void 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 | |