clang-tools  11.0.0
SizeofExpressionCheck.cpp
Go to the documentation of this file.
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 
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 
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
clang::tidy::bugprone::SizeofExpressionCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: SizeofExpressionCheck.cpp:70
E
const Expr * E
Definition: AvoidBindCheck.cpp:88
Type
NodeType Type
Definition: HTMLGenerator.cpp:73
clang::tidy::bugprone::SizeofExpressionCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: SizeofExpressionCheck.cpp:252
Ctx
Context Ctx
Definition: TUScheduler.cpp:324
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:114
SizeofExpressionCheck.h
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::readability::AST_MATCHER_P
AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl, ast_matchers::internal::Matcher< CXXMethodDecl >, InnerMatcher)
Definition: ConvertMemberFunctionsToStatic.cpp:53
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:471
Builder
CodeCompletionBuilder Builder
Definition: CodeCompletionStringsTests.cpp:35
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:76
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidyCheck.cpp:55
CE
CaptureExpr CE
Definition: AvoidBindCheck.cpp:67
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::ClangTidyCheck::OptionsView::store
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidyCheck.cpp:152
clang::tidy::bugprone::SizeofExpressionCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: SizeofExpressionCheck.cpp:79
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:111