clang-tools  9.0.0svn
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", 1) != 0),
64  WarnOnSizeOfIntegerExpression(
65  Options.get("WarnOnSizeOfIntegerExpression", 0) != 0),
66  WarnOnSizeOfThis(Options.get("WarnOnSizeOfThis", 1) != 0),
67  WarnOnSizeOfCompareToConstant(
68  Options.get("WarnOnSizeOfCompareToConstant", 1) != 0) {}
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 =
88  expr(anyOf(sizeOfExpr(has(type())), sizeOfExpr(has(expr()))));
89  const auto SizeOfZero = expr(
90  sizeOfExpr(has(ignoringParenImpCasts(expr(integerLiteral(equals(0)))))));
91 
92  // Detect expression like: sizeof(ARRAYLEN);
93  // Note: The expression 'sizeof(sizeof(0))' is a portable trick used to know
94  // the sizeof size_t.
95  if (WarnOnSizeOfConstant) {
96  Finder->addMatcher(
97  expr(sizeOfExpr(has(ignoringParenImpCasts(ConstantExpr))),
98  unless(SizeOfZero))
99  .bind("sizeof-constant"),
100  this);
101  }
102 
103  // Detect sizeof(f())
104  if (WarnOnSizeOfIntegerExpression) {
105  Finder->addMatcher(
106  expr(sizeOfExpr(ignoringParenImpCasts(has(IntegerCallExpr))))
107  .bind("sizeof-integer-call"),
108  this);
109  }
110 
111  // Detect expression like: sizeof(this);
112  if (WarnOnSizeOfThis) {
113  Finder->addMatcher(
114  expr(sizeOfExpr(has(ignoringParenImpCasts(expr(cxxThisExpr())))))
115  .bind("sizeof-this"),
116  this);
117  }
118 
119  // Detect sizeof(kPtr) where kPtr is 'const char* kPtr = "abc"';
120  const auto CharPtrType = pointerType(pointee(isAnyCharacter()));
121  const auto ConstStrLiteralDecl =
122  varDecl(isDefinition(), hasType(qualType(hasCanonicalType(CharPtrType))),
123  hasInitializer(ignoringParenImpCasts(stringLiteral())));
124  Finder->addMatcher(expr(sizeOfExpr(has(ignoringParenImpCasts(expr(
125  hasType(qualType(hasCanonicalType(CharPtrType))),
126  ignoringParenImpCasts(declRefExpr(
127  hasDeclaration(ConstStrLiteralDecl))))))))
128  .bind("sizeof-charp"),
129  this);
130 
131  // Detect sizeof(ptr) where ptr points to an aggregate (i.e. sizeof(&S)).
132  const auto ArrayExpr = expr(ignoringParenImpCasts(
133  expr(hasType(qualType(hasCanonicalType(arrayType()))))));
134  const auto ArrayCastExpr = expr(anyOf(
135  unaryOperator(hasUnaryOperand(ArrayExpr), unless(hasOperatorName("*"))),
136  binaryOperator(hasEitherOperand(ArrayExpr)),
137  castExpr(hasSourceExpression(ArrayExpr))));
138  const auto PointerToArrayExpr = expr(ignoringParenImpCasts(expr(
139  hasType(qualType(hasCanonicalType(pointerType(pointee(arrayType()))))))));
140 
141  const auto StructAddrOfExpr =
142  unaryOperator(hasOperatorName("&"),
143  hasUnaryOperand(ignoringParenImpCasts(expr(
144  hasType(qualType(hasCanonicalType(recordType())))))));
145 
146  Finder->addMatcher(
147  expr(sizeOfExpr(has(expr(ignoringParenImpCasts(
148  anyOf(ArrayCastExpr, PointerToArrayExpr, StructAddrOfExpr))))))
149  .bind("sizeof-pointer-to-aggregate"),
150  this);
151 
152  // Detect expression like: sizeof(epxr) <= k for a suspicious constant 'k'.
153  if (WarnOnSizeOfCompareToConstant) {
154  Finder->addMatcher(
155  binaryOperator(matchers::isRelationalOperator(),
156  hasEitherOperand(ignoringParenImpCasts(SizeOfExpr)),
157  hasEitherOperand(ignoringParenImpCasts(
158  anyOf(integerLiteral(equals(0)),
159  integerLiteral(isBiggerThan(0x80000))))))
160  .bind("sizeof-compare-constant"),
161  this);
162  }
163 
164  // Detect expression like: sizeof(expr, expr); most likely an error.
165  Finder->addMatcher(expr(sizeOfExpr(has(expr(ignoringParenImpCasts(
166  binaryOperator(hasOperatorName(",")))))))
167  .bind("sizeof-comma-expr"),
168  this);
169 
170  // Detect sizeof(...) /sizeof(...));
171  const auto ElemType =
172  arrayType(hasElementType(recordType().bind("elem-type")));
173  const auto ElemPtrType = pointerType(pointee(type().bind("elem-ptr-type")));
174  const auto NumType = qualType(hasCanonicalType(
175  type(anyOf(ElemType, ElemPtrType, type())).bind("num-type")));
176  const auto DenomType = qualType(hasCanonicalType(type().bind("denom-type")));
177 
178  Finder->addMatcher(
179  binaryOperator(hasOperatorName("/"),
180  hasLHS(expr(ignoringParenImpCasts(
181  anyOf(sizeOfExpr(has(NumType)),
182  sizeOfExpr(has(expr(hasType(NumType)))))))),
183  hasRHS(expr(ignoringParenImpCasts(
184  anyOf(sizeOfExpr(has(DenomType)),
185  sizeOfExpr(has(expr(hasType(DenomType)))))))))
186  .bind("sizeof-divide-expr"),
187  this);
188 
189  // Detect expression like: sizeof(...) * sizeof(...)); most likely an error.
190  Finder->addMatcher(binaryOperator(hasOperatorName("*"),
191  hasLHS(ignoringParenImpCasts(SizeOfExpr)),
192  hasRHS(ignoringParenImpCasts(SizeOfExpr)))
193  .bind("sizeof-multiply-sizeof"),
194  this);
195 
196  Finder->addMatcher(
197  binaryOperator(hasOperatorName("*"),
198  hasEitherOperand(ignoringParenImpCasts(SizeOfExpr)),
199  hasEitherOperand(ignoringParenImpCasts(binaryOperator(
200  hasOperatorName("*"),
201  hasEitherOperand(ignoringParenImpCasts(SizeOfExpr))))))
202  .bind("sizeof-multiply-sizeof"),
203  this);
204 
205  // Detect strange double-sizeof expression like: sizeof(sizeof(...));
206  // Note: The expression 'sizeof(sizeof(0))' is accepted.
207  Finder->addMatcher(
208  expr(sizeOfExpr(has(ignoringParenImpCasts(expr(
209  hasSizeOfDescendant(8, expr(SizeOfExpr, unless(SizeOfZero))))))))
210  .bind("sizeof-sizeof-expr"),
211  this);
212 }
213 
214 void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
215  const ASTContext &Ctx = *Result.Context;
216 
217  if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-constant")) {
218  diag(E->getBeginLoc(),
219  "suspicious usage of 'sizeof(K)'; did you mean 'K'?");
220  } else if (const auto *E =
221  Result.Nodes.getNodeAs<Expr>("sizeof-integer-call")) {
222  diag(E->getBeginLoc(), "suspicious usage of 'sizeof()' on an expression "
223  "that results in an integer");
224  } else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-this")) {
225  diag(E->getBeginLoc(),
226  "suspicious usage of 'sizeof(this)'; did you mean 'sizeof(*this)'");
227  } else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-charp")) {
228  diag(E->getBeginLoc(),
229  "suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?");
230  } else if (const auto *E =
231  Result.Nodes.getNodeAs<Expr>("sizeof-pointer-to-aggregate")) {
232  diag(E->getBeginLoc(),
233  "suspicious usage of 'sizeof(A*)'; pointer to aggregate");
234  } else if (const auto *E =
235  Result.Nodes.getNodeAs<Expr>("sizeof-compare-constant")) {
236  diag(E->getBeginLoc(),
237  "suspicious comparison of 'sizeof(expr)' to a constant");
238  } else if (const auto *E =
239  Result.Nodes.getNodeAs<Expr>("sizeof-comma-expr")) {
240  diag(E->getBeginLoc(), "suspicious usage of 'sizeof(..., ...)'");
241  } else if (const auto *E =
242  Result.Nodes.getNodeAs<Expr>("sizeof-divide-expr")) {
243  const auto *NumTy = Result.Nodes.getNodeAs<Type>("num-type");
244  const auto *DenomTy = Result.Nodes.getNodeAs<Type>("denom-type");
245  const auto *ElementTy = Result.Nodes.getNodeAs<Type>("elem-type");
246  const auto *PointedTy = Result.Nodes.getNodeAs<Type>("elem-ptr-type");
247 
248  CharUnits NumeratorSize = getSizeOfType(Ctx, NumTy);
249  CharUnits DenominatorSize = getSizeOfType(Ctx, DenomTy);
250  CharUnits ElementSize = getSizeOfType(Ctx, ElementTy);
251 
252  if (DenominatorSize > CharUnits::Zero() &&
253  !NumeratorSize.isMultipleOf(DenominatorSize)) {
254  diag(E->getBeginLoc(), "suspicious usage of 'sizeof(...)/sizeof(...)';"
255  " numerator is not a multiple of denominator");
256  } else if (ElementSize > CharUnits::Zero() &&
257  DenominatorSize > CharUnits::Zero() &&
258  ElementSize != DenominatorSize) {
259  diag(E->getBeginLoc(), "suspicious usage of 'sizeof(...)/sizeof(...)';"
260  " numerator is not a multiple of denominator");
261  } else if (NumTy && DenomTy && NumTy == DenomTy) {
262  diag(E->getBeginLoc(),
263  "suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'");
264  } else if (PointedTy && DenomTy && PointedTy == DenomTy) {
265  diag(E->getBeginLoc(),
266  "suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'");
267  } else if (NumTy && DenomTy && NumTy->isPointerType() &&
268  DenomTy->isPointerType()) {
269  diag(E->getBeginLoc(),
270  "suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'");
271  }
272  } else if (const auto *E =
273  Result.Nodes.getNodeAs<Expr>("sizeof-sizeof-expr")) {
274  diag(E->getBeginLoc(), "suspicious usage of 'sizeof(sizeof(...))'");
275  } else if (const auto *E =
276  Result.Nodes.getNodeAs<Expr>("sizeof-multiply-sizeof")) {
277  diag(E->getBeginLoc(), "suspicious 'sizeof' by 'sizeof' multiplication");
278  }
279 }
280 
281 } // namespace bugprone
282 } // namespace tidy
283 } // namespace clang
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
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: ClangTidy.cpp:472
Base class for all clang-tidy checks.
Definition: ClangTidy.h:126
Context Ctx
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
AST_MATCHER_P(FunctionDecl, throws, internal::Matcher< Type >, InnerMatcher)
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:437