10#include "../utils/Matchers.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
21 return Node.getValue().ugt(N);
24AST_MATCHER_P2(Expr, hasSizeOfDescendant,
int, Depth,
25 ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
29 const Expr *
E =
Node.IgnoreParenImpCasts();
30 if (InnerMatcher.matches(*
E, Finder,
Builder))
33 if (
const auto *
CE = dyn_cast<CastExpr>(
E)) {
34 const auto M = hasSizeOfDescendant(Depth - 1, InnerMatcher);
35 return M.matches(*
CE->getSubExpr(), Finder,
Builder);
37 if (
const auto *UE = dyn_cast<UnaryOperator>(
E)) {
38 const auto M = hasSizeOfDescendant(Depth - 1, InnerMatcher);
39 return M.matches(*UE->getSubExpr(), Finder,
Builder);
41 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);
51CharUnits 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);
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 WarnOnSizeOfPointerToAggregate(
70 Options.get(
"WarnOnSizeOfPointerToAggregate", true)) {}
73 Options.
store(Opts,
"WarnOnSizeOfConstant", WarnOnSizeOfConstant);
75 WarnOnSizeOfIntegerExpression);
76 Options.
store(Opts,
"WarnOnSizeOfThis", WarnOnSizeOfThis);
78 WarnOnSizeOfCompareToConstant);
80 WarnOnSizeOfPointerToAggregate);
88 const auto IntegerExpr = ignoringParenImpCasts(integerLiteral());
89 const auto ConstantExpr = ignoringParenImpCasts(
90 anyOf(integerLiteral(), unaryOperator(hasUnaryOperand(IntegerExpr)),
91 binaryOperator(hasLHS(IntegerExpr), hasRHS(IntegerExpr))));
92 const auto IntegerCallExpr = ignoringParenImpCasts(callExpr(
93 anyOf(hasType(isInteger()), hasType(hasCanonicalType(enumType()))),
94 unless(isInTemplateInstantiation())));
95 const auto SizeOfExpr = sizeOfExpr(hasArgumentOfType(
96 hasUnqualifiedDesugaredType(type().bind(
"sizeof-arg-type"))));
97 const auto SizeOfZero =
98 sizeOfExpr(has(ignoringParenImpCasts(integerLiteral(equals(0)))));
103 if (WarnOnSizeOfConstant) {
105 expr(sizeOfExpr(has(ignoringParenImpCasts(ConstantExpr))),
107 .bind(
"sizeof-constant"),
112 if (WarnOnSizeOfIntegerExpression) {
113 Finder->addMatcher(sizeOfExpr(ignoringParenImpCasts(has(IntegerCallExpr)))
114 .bind(
"sizeof-integer-call"),
119 if (WarnOnSizeOfThis) {
120 Finder->addMatcher(sizeOfExpr(has(ignoringParenImpCasts(cxxThisExpr())))
121 .bind(
"sizeof-this"),
126 const auto CharPtrType = pointerType(pointee(isAnyCharacter()));
127 const auto ConstStrLiteralDecl =
128 varDecl(isDefinition(), hasType(hasCanonicalType(CharPtrType)),
129 hasInitializer(ignoringParenImpCasts(stringLiteral())));
131 sizeOfExpr(has(ignoringParenImpCasts(
132 expr(hasType(hasCanonicalType(CharPtrType)),
133 ignoringParenImpCasts(declRefExpr(
134 hasDeclaration(ConstStrLiteralDecl)))))))
135 .bind(
"sizeof-charp"),
140 if (WarnOnSizeOfPointerToAggregate) {
141 const auto ArrayExpr =
142 ignoringParenImpCasts(hasType(hasCanonicalType(arrayType())));
143 const auto ArrayCastExpr = expr(anyOf(
144 unaryOperator(hasUnaryOperand(ArrayExpr), unless(hasOperatorName(
"*"))),
145 binaryOperator(hasEitherOperand(ArrayExpr)),
146 castExpr(hasSourceExpression(ArrayExpr))));
147 const auto PointerToArrayExpr = ignoringParenImpCasts(
148 hasType(hasCanonicalType(pointerType(pointee(arrayType())))));
150 const auto StructAddrOfExpr = unaryOperator(
151 hasOperatorName(
"&"), hasUnaryOperand(ignoringParenImpCasts(
152 hasType(hasCanonicalType(recordType())))));
153 const auto PointerToStructType =
154 hasUnqualifiedDesugaredType(pointerType(pointee(recordType())));
155 const auto PointerToStructExpr = ignoringParenImpCasts(expr(
156 hasType(hasCanonicalType(PointerToStructType)), unless(cxxThisExpr())));
158 const auto ArrayOfPointersExpr = ignoringParenImpCasts(
159 hasType(hasCanonicalType(arrayType(hasElementType(pointerType()))
160 .bind(
"type-of-array-of-pointers"))));
161 const auto ArrayOfSamePointersExpr =
162 ignoringParenImpCasts(hasType(hasCanonicalType(
163 arrayType(equalsBoundNode(
"type-of-array-of-pointers")))));
164 const auto ZeroLiteral = ignoringParenImpCasts(integerLiteral(equals(0)));
165 const auto ArrayOfSamePointersZeroSubscriptExpr =
166 ignoringParenImpCasts(arraySubscriptExpr(
167 hasBase(ArrayOfSamePointersExpr), hasIndex(ZeroLiteral)));
168 const auto ArrayLengthExprDenom =
169 expr(hasParent(expr(ignoringParenImpCasts(binaryOperator(
170 hasOperatorName(
"/"), hasLHS(ignoringParenImpCasts(sizeOfExpr(
171 has(ArrayOfPointersExpr)))))))),
172 sizeOfExpr(has(ArrayOfSamePointersZeroSubscriptExpr)));
174 Finder->addMatcher(expr(anyOf(sizeOfExpr(has(ignoringParenImpCasts(anyOf(
175 ArrayCastExpr, PointerToArrayExpr,
176 StructAddrOfExpr, PointerToStructExpr)))),
177 sizeOfExpr(has(PointerToStructType))),
178 unless(ArrayLengthExprDenom))
179 .bind(
"sizeof-pointer-to-aggregate"),
184 if (WarnOnSizeOfCompareToConstant) {
186 binaryOperator(matchers::isRelationalOperator(),
187 hasOperands(ignoringParenImpCasts(SizeOfExpr),
188 ignoringParenImpCasts(integerLiteral(anyOf(
189 equals(0), isBiggerThan(0x80000))))))
190 .bind(
"sizeof-compare-constant"),
197 has(ignoringParenImpCasts(
198 binaryOperator(hasOperatorName(
",")).bind(
"sizeof-comma-binop"))))
199 .bind(
"sizeof-comma-expr"),
209 const auto ElemType =
210 arrayType(hasElementType(recordType().bind(
"elem-type")));
211 const auto ElemPtrType = pointerType(pointee(type().bind(
"elem-ptr-type")));
215 hasOperatorName(
"/"),
216 hasLHS(ignoringParenImpCasts(sizeOfExpr(hasArgumentOfType(
217 hasCanonicalType(type(anyOf(ElemType, ElemPtrType, type()))
218 .bind(
"num-type")))))),
219 hasRHS(ignoringParenImpCasts(sizeOfExpr(
220 hasArgumentOfType(hasCanonicalType(type().bind(
"denom-type")))))))
221 .bind(
"sizeof-divide-expr"),
225 Finder->addMatcher(binaryOperator(hasOperatorName(
"*"),
226 hasLHS(ignoringParenImpCasts(SizeOfExpr)),
227 hasRHS(ignoringParenImpCasts(SizeOfExpr)))
228 .bind(
"sizeof-multiply-sizeof"),
232 binaryOperator(hasOperatorName(
"*"),
233 hasOperands(ignoringParenImpCasts(SizeOfExpr),
234 ignoringParenImpCasts(binaryOperator(
235 hasOperatorName(
"*"),
237 ignoringParenImpCasts(SizeOfExpr))))))
238 .bind(
"sizeof-multiply-sizeof"),
243 Finder->addMatcher(sizeOfExpr(has(ignoringParenImpCasts(hasSizeOfDescendant(
244 8, allOf(SizeOfExpr, unless(SizeOfZero))))))
245 .bind(
"sizeof-sizeof-expr"),
250 const auto PtrDiffExpr = binaryOperator(
251 hasOperatorName(
"-"),
252 hasLHS(hasType(hasUnqualifiedDesugaredType(pointerType(pointee(
253 hasUnqualifiedDesugaredType(type().bind(
"left-ptr-type"))))))),
254 hasRHS(hasType(hasUnqualifiedDesugaredType(pointerType(pointee(
255 hasUnqualifiedDesugaredType(type().bind(
"right-ptr-type"))))))));
259 hasAnyOperatorName(
"==",
"!=",
"<",
"<=",
">",
">=",
"+",
"-"),
260 hasOperands(anyOf(ignoringParenImpCasts(
261 SizeOfExpr.bind(
"sizeof-ptr-mul-expr")),
262 ignoringParenImpCasts(binaryOperator(
263 hasOperatorName(
"*"),
264 hasEitherOperand(ignoringParenImpCasts(
265 SizeOfExpr.bind(
"sizeof-ptr-mul-expr")))))),
266 ignoringParenImpCasts(PtrDiffExpr)))
267 .bind(
"sizeof-in-ptr-arithmetic-mul"),
272 hasOperatorName(
"/"), hasLHS(ignoringParenImpCasts(PtrDiffExpr)),
273 hasRHS(ignoringParenImpCasts(SizeOfExpr.bind(
"sizeof-ptr-div-expr"))))
274 .bind(
"sizeof-in-ptr-arithmetic-div"),
279 const ASTContext &Ctx = *Result.Context;
281 if (
const auto *
E = Result.Nodes.getNodeAs<Expr>(
"sizeof-constant")) {
282 diag(
E->getBeginLoc(),
"suspicious usage of 'sizeof(K)'; did you mean 'K'?")
283 <<
E->getSourceRange();
284 }
else if (
const auto *
E =
285 Result.Nodes.getNodeAs<Expr>(
"sizeof-integer-call")) {
286 diag(
E->getBeginLoc(),
"suspicious usage of 'sizeof()' on an expression "
287 "that results in an integer")
288 <<
E->getSourceRange();
289 }
else if (
const auto *
E = Result.Nodes.getNodeAs<Expr>(
"sizeof-this")) {
290 diag(
E->getBeginLoc(),
291 "suspicious usage of 'sizeof(this)'; did you mean 'sizeof(*this)'")
292 <<
E->getSourceRange();
293 }
else if (
const auto *
E = Result.Nodes.getNodeAs<Expr>(
"sizeof-charp")) {
294 diag(
E->getBeginLoc(),
295 "suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?")
296 <<
E->getSourceRange();
297 }
else if (
const auto *
E =
298 Result.Nodes.getNodeAs<Expr>(
"sizeof-pointer-to-aggregate")) {
299 diag(
E->getBeginLoc(),
300 "suspicious usage of 'sizeof(A*)'; pointer to aggregate")
301 <<
E->getSourceRange();
302 }
else if (
const auto *
E = Result.Nodes.getNodeAs<BinaryOperator>(
303 "sizeof-compare-constant")) {
304 diag(
E->getOperatorLoc(),
305 "suspicious comparison of 'sizeof(expr)' to a constant")
306 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
307 }
else if (
const auto *
E =
308 Result.Nodes.getNodeAs<Expr>(
"sizeof-comma-expr")) {
310 Result.Nodes.getNodeAs<BinaryOperator>(
"sizeof-comma-binop");
312 diag(BO->getOperatorLoc(),
"suspicious usage of 'sizeof(..., ...)'")
313 <<
E->getSourceRange();
314 }
else if (
const auto *
E =
315 Result.Nodes.getNodeAs<BinaryOperator>(
"sizeof-divide-expr")) {
316 const auto *NumTy = Result.Nodes.getNodeAs<
Type>(
"num-type");
317 const auto *DenomTy = Result.Nodes.getNodeAs<
Type>(
"denom-type");
318 const auto *ElementTy = Result.Nodes.getNodeAs<
Type>(
"elem-type");
319 const auto *PointedTy = Result.Nodes.getNodeAs<
Type>(
"elem-ptr-type");
321 CharUnits NumeratorSize = getSizeOfType(Ctx, NumTy);
322 CharUnits DenominatorSize = getSizeOfType(Ctx, DenomTy);
323 CharUnits ElementSize = getSizeOfType(Ctx, ElementTy);
325 if (DenominatorSize > CharUnits::Zero() &&
326 !NumeratorSize.isMultipleOf(DenominatorSize)) {
327 diag(
E->getOperatorLoc(),
"suspicious usage of 'sizeof(...)/sizeof(...)';"
328 " numerator is not a multiple of denominator")
329 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
330 }
else if (ElementSize > CharUnits::Zero() &&
331 DenominatorSize > CharUnits::Zero() &&
332 ElementSize != DenominatorSize) {
333 diag(
E->getOperatorLoc(),
"suspicious usage of 'sizeof(...)/sizeof(...)';"
334 " numerator is not a multiple of denominator")
335 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
336 }
else if (NumTy && DenomTy && NumTy == DenomTy) {
337 diag(
E->getOperatorLoc(),
338 "suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'")
339 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
340 }
else if (PointedTy && DenomTy && PointedTy == DenomTy) {
341 diag(
E->getOperatorLoc(),
342 "suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'")
343 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
344 }
else if (NumTy && DenomTy && NumTy->isPointerType() &&
345 DenomTy->isPointerType()) {
346 diag(
E->getOperatorLoc(),
347 "suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'")
348 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
350 }
else if (
const auto *
E =
351 Result.Nodes.getNodeAs<Expr>(
"sizeof-sizeof-expr")) {
352 diag(
E->getBeginLoc(),
"suspicious usage of 'sizeof(sizeof(...))'")
353 <<
E->getSourceRange();
354 }
else if (
const auto *
E = Result.Nodes.getNodeAs<BinaryOperator>(
355 "sizeof-multiply-sizeof")) {
356 diag(
E->getOperatorLoc(),
"suspicious 'sizeof' by 'sizeof' multiplication")
357 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
358 }
else if (
const auto *
E = Result.Nodes.getNodeAs<BinaryOperator>(
359 "sizeof-in-ptr-arithmetic-mul")) {
360 const auto *LPtrTy = Result.Nodes.getNodeAs<
Type>(
"left-ptr-type");
361 const auto *RPtrTy = Result.Nodes.getNodeAs<
Type>(
"right-ptr-type");
362 const auto *SizeofArgTy = Result.Nodes.getNodeAs<
Type>(
"sizeof-arg-type");
363 const auto *SizeOfExpr =
364 Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>(
"sizeof-ptr-mul-expr");
366 if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) {
367 diag(SizeOfExpr->getBeginLoc(),
"suspicious usage of 'sizeof(...)' in "
368 "pointer arithmetic")
369 << SizeOfExpr->getSourceRange() <<
E->getOperatorLoc()
370 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
372 }
else if (
const auto *
E = Result.Nodes.getNodeAs<BinaryOperator>(
373 "sizeof-in-ptr-arithmetic-div")) {
374 const auto *LPtrTy = Result.Nodes.getNodeAs<
Type>(
"left-ptr-type");
375 const auto *RPtrTy = Result.Nodes.getNodeAs<
Type>(
"right-ptr-type");
376 const auto *SizeofArgTy = Result.Nodes.getNodeAs<
Type>(
"sizeof-arg-type");
377 const auto *SizeOfExpr =
378 Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>(
"sizeof-ptr-div-expr");
380 if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) {
381 diag(SizeOfExpr->getBeginLoc(),
"suspicious usage of 'sizeof(...)' in "
382 "pointer arithmetic")
383 << SizeOfExpr->getSourceRange() <<
E->getOperatorLoc()
384 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
llvm::SmallString< 256U > Name
CodeCompletionBuilder Builder
::clang::DynTypedNode Node
const google::protobuf::Message & M
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.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
SizeofExpressionCheck(StringRef Name, ClangTidyContext *Context)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
AST_MATCHER_P(FunctionDecl, parameterCountGE, unsigned, N)
Matches functions that have at least the specified amount of parameters.
llvm::StringMap< ClangTidyValue > OptionMap