39526dab79ae32a3a9a9805ea628c27251cb1855
[lldb.git] / clang-tools-extra / clang-tidy / bugprone / SizeofExpressionCheck.cpp
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 {
17 namespace tidy {
18 namespace bugprone {
19
20 namespace {
21
22 AST_MATCHER_P(IntegerLiteral, isBiggerThan, unsigned, N) {
23   return Node.getValue().getZExtValue() > N;
24 }
25
26 AST_MATCHER_P2(Expr, hasSizeOfDescendant, int, Depth,
27                ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
28   if (Depth < 0)
29     return false;
30
31   const Expr *E = Node.IgnoreParenImpCasts();
32   if (InnerMatcher.matches(*E, Finder, Builder))
33     return true;
34
35   if (const auto *CE = dyn_cast<CastExpr>(E)) {
36     const auto M = hasSizeOfDescendant(Depth - 1, InnerMatcher);
37     return M.matches(*CE->getSubExpr(), Finder, Builder);
38   } else if (const auto *UE = dyn_cast<UnaryOperator>(E)) {
39     const auto M = hasSizeOfDescendant(Depth - 1, InnerMatcher);
40     return M.matches(*UE->getSubExpr(), Finder, Builder);
41   } else 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 CharUnits getSizeOfType(const ASTContext &Ctx, const Type *Ty) {
52   if (!Ty || Ty->isIncompleteType() || Ty->isDependentType() ||
53       isa<DependentSizedArrayType>(Ty) || !Ty->isConstantSizeType())
54     return CharUnits::Zero();
55   return Ctx.getTypeSizeInChars(Ty);
56 }
57
58 } // namespace
59
60 SizeofExpressionCheck::SizeofExpressionCheck(StringRef Name,
61                                              ClangTidyContext *Context)
62     : ClangTidyCheck(Name, Context),
63       WarnOnSizeOfConstant(Options.get("WarnOnSizeOfConstant", true)),
64       WarnOnSizeOfIntegerExpression(
65           Options.get("WarnOnSizeOfIntegerExpression", false)),
66       WarnOnSizeOfThis(Options.get("WarnOnSizeOfThis", true)),
67       WarnOnSizeOfCompareToConstant(
68           Options.get("WarnOnSizeOfCompareToConstant", true)) {}
69
70 void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
71   Options.store(Opts, "WarnOnSizeOfConstant", WarnOnSizeOfConstant);
72   Options.store(Opts, "WarnOnSizeOfIntegerExpression",
73                 WarnOnSizeOfIntegerExpression);
74   Options.store(Opts, "WarnOnSizeOfThis", WarnOnSizeOfThis);
75   Options.store(Opts, "WarnOnSizeOfCompareToConstant",
76                 WarnOnSizeOfCompareToConstant);
77 }
78
79 void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
80   const auto IntegerExpr = ignoringParenImpCasts(integerLiteral());
81   const auto ConstantExpr = expr(ignoringParenImpCasts(
82       anyOf(integerLiteral(), unaryOperator(hasUnaryOperand(IntegerExpr)),
83             binaryOperator(hasLHS(IntegerExpr), hasRHS(IntegerExpr)))));
84   const auto IntegerCallExpr = expr(ignoringParenImpCasts(
85       callExpr(anyOf(hasType(isInteger()), hasType(enumType())),
86                unless(isInTemplateInstantiation()))));
87   const auto SizeOfExpr = expr(anyOf(
88       sizeOfExpr(
89           has(hasUnqualifiedDesugaredType(type().bind("sizeof-arg-type")))),
90       sizeOfExpr(has(expr(hasType(
91           hasUnqualifiedDesugaredType(type().bind("sizeof-arg-type"))))))));
92   const auto SizeOfZero = expr(
93       sizeOfExpr(has(ignoringParenImpCasts(expr(integerLiteral(equals(0)))))));
94
95   // Detect expression like: sizeof(ARRAYLEN);
96   // Note: The expression 'sizeof(sizeof(0))' is a portable trick used to know
97   //       the sizeof size_t.
98   if (WarnOnSizeOfConstant) {
99     Finder->addMatcher(
100         expr(sizeOfExpr(has(ignoringParenImpCasts(ConstantExpr))),
101              unless(SizeOfZero))
102             .bind("sizeof-constant"),
103         this);
104   }
105
106   // Detect sizeof(f())
107   if (WarnOnSizeOfIntegerExpression) {
108     Finder->addMatcher(
109         expr(sizeOfExpr(ignoringParenImpCasts(has(IntegerCallExpr))))
110             .bind("sizeof-integer-call"),
111         this);
112   }
113
114   // Detect expression like: sizeof(this);
115   if (WarnOnSizeOfThis) {
116     Finder->addMatcher(
117         expr(sizeOfExpr(has(ignoringParenImpCasts(expr(cxxThisExpr())))))
118             .bind("sizeof-this"),
119         this);
120   }
121
122   // Detect sizeof(kPtr) where kPtr is 'const char* kPtr = "abc"';
123   const auto CharPtrType = pointerType(pointee(isAnyCharacter()));
124   const auto ConstStrLiteralDecl =
125       varDecl(isDefinition(), hasType(qualType(hasCanonicalType(CharPtrType))),
126               hasInitializer(ignoringParenImpCasts(stringLiteral())));
127   Finder->addMatcher(expr(sizeOfExpr(has(ignoringParenImpCasts(expr(
128                               hasType(qualType(hasCanonicalType(CharPtrType))),
129                               ignoringParenImpCasts(declRefExpr(
130                                   hasDeclaration(ConstStrLiteralDecl))))))))
131                          .bind("sizeof-charp"),
132                      this);
133
134   // Detect sizeof(ptr) where ptr points to an aggregate (i.e. sizeof(&S)).
135   const auto ArrayExpr = expr(ignoringParenImpCasts(
136       expr(hasType(qualType(hasCanonicalType(arrayType()))))));
137   const auto ArrayCastExpr = expr(anyOf(
138       unaryOperator(hasUnaryOperand(ArrayExpr), unless(hasOperatorName("*"))),
139       binaryOperator(hasEitherOperand(ArrayExpr)),
140       castExpr(hasSourceExpression(ArrayExpr))));
141   const auto PointerToArrayExpr = expr(ignoringParenImpCasts(expr(
142       hasType(qualType(hasCanonicalType(pointerType(pointee(arrayType()))))))));
143
144   const auto StructAddrOfExpr =
145       unaryOperator(hasOperatorName("&"),
146                     hasUnaryOperand(ignoringParenImpCasts(expr(
147                         hasType(qualType(hasCanonicalType(recordType())))))));
148   const auto PointerToStructType = type(hasUnqualifiedDesugaredType(
149       pointerType(pointee(recordType()))));
150   const auto PointerToStructExpr = expr(ignoringParenImpCasts(expr(
151       hasType(qualType(hasCanonicalType(PointerToStructType))),
152       unless(cxxThisExpr()))));
153
154   Finder->addMatcher(
155       expr(anyOf(sizeOfExpr(has(expr(ignoringParenImpCasts(
156                anyOf(ArrayCastExpr, PointerToArrayExpr, StructAddrOfExpr,
157                      PointerToStructExpr))))),
158                             sizeOfExpr(has(PointerToStructType))))
159           .bind("sizeof-pointer-to-aggregate"),
160       this);
161
162   // Detect expression like: sizeof(epxr) <= k for a suspicious constant 'k'.
163   if (WarnOnSizeOfCompareToConstant) {
164     Finder->addMatcher(
165         binaryOperator(matchers::isRelationalOperator(),
166                        hasOperands(ignoringParenImpCasts(SizeOfExpr),
167                                    ignoringParenImpCasts(anyOf(
168                                        integerLiteral(equals(0)),
169                                        integerLiteral(isBiggerThan(0x80000))))))
170             .bind("sizeof-compare-constant"),
171         this);
172   }
173
174   // Detect expression like: sizeof(expr, expr); most likely an error.
175   Finder->addMatcher(expr(sizeOfExpr(has(expr(ignoringParenImpCasts(
176                               binaryOperator(hasOperatorName(",")))))))
177                          .bind("sizeof-comma-expr"),
178                      this);
179
180   // Detect sizeof(...) /sizeof(...));
181   const auto ElemType =
182       arrayType(hasElementType(recordType().bind("elem-type")));
183   const auto ElemPtrType = pointerType(pointee(type().bind("elem-ptr-type")));
184   const auto NumType = qualType(hasCanonicalType(
185       type(anyOf(ElemType, ElemPtrType, type())).bind("num-type")));
186   const auto DenomType = qualType(hasCanonicalType(type().bind("denom-type")));
187
188   Finder->addMatcher(
189       binaryOperator(hasOperatorName("/"),
190                      hasLHS(expr(ignoringParenImpCasts(
191                          anyOf(sizeOfExpr(has(NumType)),
192                                sizeOfExpr(has(expr(hasType(NumType)))))))),
193                      hasRHS(expr(ignoringParenImpCasts(
194                          anyOf(sizeOfExpr(has(DenomType)),
195                                sizeOfExpr(has(expr(hasType(DenomType)))))))))
196           .bind("sizeof-divide-expr"),
197       this);
198
199   // Detect expression like: sizeof(...) * sizeof(...)); most likely an error.
200   Finder->addMatcher(binaryOperator(hasOperatorName("*"),
201                                     hasLHS(ignoringParenImpCasts(SizeOfExpr)),
202                                     hasRHS(ignoringParenImpCasts(SizeOfExpr)))
203                          .bind("sizeof-multiply-sizeof"),
204                      this);
205
206   Finder->addMatcher(
207       binaryOperator(hasOperatorName("*"),
208                      hasOperands(ignoringParenImpCasts(SizeOfExpr),
209                                  ignoringParenImpCasts(binaryOperator(
210                                      hasOperatorName("*"),
211                                      hasEitherOperand(
212                                          ignoringParenImpCasts(SizeOfExpr))))))
213           .bind("sizeof-multiply-sizeof"),
214       this);
215
216   // Detect strange double-sizeof expression like: sizeof(sizeof(...));
217   // Note: The expression 'sizeof(sizeof(0))' is accepted.
218   Finder->addMatcher(
219       expr(sizeOfExpr(has(ignoringParenImpCasts(expr(
220                hasSizeOfDescendant(8, expr(SizeOfExpr, unless(SizeOfZero))))))))
221           .bind("sizeof-sizeof-expr"),
222       this);
223
224   // Detect sizeof in pointer arithmetic like: N * sizeof(S) == P1 - P2 or
225   // (P1 - P2) / sizeof(S) where P1 and P2 are pointers to type S.
226   const auto PtrDiffExpr = binaryOperator(
227       hasOperatorName("-"),
228       hasLHS(expr(hasType(hasUnqualifiedDesugaredType(pointerType(pointee(
229           hasUnqualifiedDesugaredType(type().bind("left-ptr-type")))))))),
230       hasRHS(expr(hasType(hasUnqualifiedDesugaredType(pointerType(pointee(
231           hasUnqualifiedDesugaredType(type().bind("right-ptr-type")))))))));
232
233   Finder->addMatcher(
234       binaryOperator(
235           hasAnyOperatorName("==", "!=", "<", "<=", ">", ">=", "+", "-"),
236           hasOperands(expr(anyOf(ignoringParenImpCasts(SizeOfExpr),
237                                  ignoringParenImpCasts(binaryOperator(
238                                      hasOperatorName("*"),
239                                      hasEitherOperand(
240                                          ignoringParenImpCasts(SizeOfExpr)))))),
241                       ignoringParenImpCasts(PtrDiffExpr)))
242           .bind("sizeof-in-ptr-arithmetic-mul"),
243       this);
244
245   Finder->addMatcher(binaryOperator(hasOperatorName("/"),
246                                     hasLHS(ignoringParenImpCasts(PtrDiffExpr)),
247                                     hasRHS(ignoringParenImpCasts(SizeOfExpr)))
248                          .bind("sizeof-in-ptr-arithmetic-div"),
249                      this);
250 }
251
252 void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
253   const ASTContext &Ctx = *Result.Context;
254
255   if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-constant")) {
256     diag(E->getBeginLoc(),
257          "suspicious usage of 'sizeof(K)'; did you mean 'K'?");
258   } else if (const auto *E =
259                  Result.Nodes.getNodeAs<Expr>("sizeof-integer-call")) {
260     diag(E->getBeginLoc(), "suspicious usage of 'sizeof()' on an expression "
261                            "that results in an integer");
262   } else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-this")) {
263     diag(E->getBeginLoc(),
264          "suspicious usage of 'sizeof(this)'; did you mean 'sizeof(*this)'");
265   } else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-charp")) {
266     diag(E->getBeginLoc(),
267          "suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?");
268   } else if (const auto *E =
269                  Result.Nodes.getNodeAs<Expr>("sizeof-pointer-to-aggregate")) {
270     diag(E->getBeginLoc(),
271          "suspicious usage of 'sizeof(A*)'; pointer to aggregate");
272   } else if (const auto *E =
273                  Result.Nodes.getNodeAs<Expr>("sizeof-compare-constant")) {
274     diag(E->getBeginLoc(),
275          "suspicious comparison of 'sizeof(expr)' to a constant");
276   } else if (const auto *E =
277                  Result.Nodes.getNodeAs<Expr>("sizeof-comma-expr")) {
278     diag(E->getBeginLoc(), "suspicious usage of 'sizeof(..., ...)'");
279   } else if (const auto *E =
280                  Result.Nodes.getNodeAs<Expr>("sizeof-divide-expr")) {
281     const auto *NumTy = Result.Nodes.getNodeAs<Type>("num-type");
282     const auto *DenomTy = Result.Nodes.getNodeAs<Type>("denom-type");
283     const auto *ElementTy = Result.Nodes.getNodeAs<Type>("elem-type");
284     const auto *PointedTy = Result.Nodes.getNodeAs<Type>("elem-ptr-type");
285
286     CharUnits NumeratorSize = getSizeOfType(Ctx, NumTy);
287     CharUnits DenominatorSize = getSizeOfType(Ctx, DenomTy);
288     CharUnits ElementSize = getSizeOfType(Ctx, ElementTy);
289
290     if (DenominatorSize > CharUnits::Zero() &&
291         !NumeratorSize.isMultipleOf(DenominatorSize)) {
292       diag(E->getBeginLoc(), "suspicious usage of 'sizeof(...)/sizeof(...)';"
293                              " numerator is not a multiple of denominator");
294     } else if (ElementSize > CharUnits::Zero() &&
295                DenominatorSize > CharUnits::Zero() &&
296                ElementSize != DenominatorSize) {
297       diag(E->getBeginLoc(), "suspicious usage of 'sizeof(...)/sizeof(...)';"
298                              " numerator is not a multiple of denominator");
299     } else if (NumTy && DenomTy && NumTy == DenomTy) {
300       diag(E->getBeginLoc(),
301            "suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'");
302     } else if (PointedTy && DenomTy && PointedTy == DenomTy) {
303       diag(E->getBeginLoc(),
304            "suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'");
305     } else if (NumTy && DenomTy && NumTy->isPointerType() &&
306                DenomTy->isPointerType()) {
307       diag(E->getBeginLoc(),
308            "suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'");
309     }
310   } else if (const auto *E =
311                  Result.Nodes.getNodeAs<Expr>("sizeof-sizeof-expr")) {
312     diag(E->getBeginLoc(), "suspicious usage of 'sizeof(sizeof(...))'");
313   } else if (const auto *E =
314                  Result.Nodes.getNodeAs<Expr>("sizeof-multiply-sizeof")) {
315     diag(E->getBeginLoc(), "suspicious 'sizeof' by 'sizeof' multiplication");
316   } else if (const auto *E =
317                  Result.Nodes.getNodeAs<Expr>("sizeof-in-ptr-arithmetic-mul")) {
318     const auto *LPtrTy = Result.Nodes.getNodeAs<Type>("left-ptr-type");
319     const auto *RPtrTy = Result.Nodes.getNodeAs<Type>("right-ptr-type");
320     const auto *SizeofArgTy = Result.Nodes.getNodeAs<Type>("sizeof-arg-type");
321
322     if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) {
323       diag(E->getBeginLoc(), "suspicious usage of 'sizeof(...)' in "
324                               "pointer arithmetic");
325     }
326   } else if (const auto *E =
327                  Result.Nodes.getNodeAs<Expr>("sizeof-in-ptr-arithmetic-div")) {
328     const auto *LPtrTy = Result.Nodes.getNodeAs<Type>("left-ptr-type");
329     const auto *RPtrTy = Result.Nodes.getNodeAs<Type>("right-ptr-type");
330     const auto *SizeofArgTy = Result.Nodes.getNodeAs<Type>("sizeof-arg-type");
331
332     if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) {
333       diag(E->getBeginLoc(), "suspicious usage of 'sizeof(...)' in "
334                               "pointer arithmetic");
335     }
336   }
337 }
338
339 } // namespace bugprone
340 } // namespace tidy
341 } // namespace clang