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);
51AST_MATCHER(Expr, offsetOfExpr) {
return isa<OffsetOfExpr>(Node); }
53CharUnits getSizeOfType(
const ASTContext &Ctx,
const Type *Ty) {
54 if (!Ty || Ty->isIncompleteType() || Ty->isDependentType() ||
55 isa<DependentSizedArrayType>(Ty) || !Ty->isConstantSizeType())
56 return CharUnits::Zero();
57 return Ctx.getTypeSizeInChars(Ty);
65 WarnOnSizeOfConstant(Options.get(
"WarnOnSizeOfConstant", true)),
66 WarnOnSizeOfIntegerExpression(
67 Options.get(
"WarnOnSizeOfIntegerExpression", false)),
68 WarnOnSizeOfThis(Options.get(
"WarnOnSizeOfThis", true)),
69 WarnOnSizeOfCompareToConstant(
70 Options.get(
"WarnOnSizeOfCompareToConstant", true)),
71 WarnOnSizeOfPointerToAggregate(
72 Options.get(
"WarnOnSizeOfPointerToAggregate", true)),
73 WarnOnSizeOfPointer(Options.get(
"WarnOnSizeOfPointer", false)),
74 WarnOnOffsetDividedBySizeOf(
75 Options.get(
"WarnOnOffsetDividedBySizeOf", true)) {}
78 Options.
store(Opts,
"WarnOnSizeOfConstant", WarnOnSizeOfConstant);
80 WarnOnSizeOfIntegerExpression);
81 Options.
store(Opts,
"WarnOnSizeOfThis", WarnOnSizeOfThis);
83 WarnOnSizeOfCompareToConstant);
85 WarnOnSizeOfPointerToAggregate);
86 Options.
store(Opts,
"WarnOnSizeOfPointer", WarnOnSizeOfPointer);
88 WarnOnOffsetDividedBySizeOf);
96 const auto IntegerExpr = ignoringParenImpCasts(integerLiteral());
97 const auto ConstantExpr = ignoringParenImpCasts(
98 anyOf(integerLiteral(), unaryOperator(hasUnaryOperand(IntegerExpr)),
99 binaryOperator(hasLHS(IntegerExpr), hasRHS(IntegerExpr))));
100 const auto IntegerCallExpr = ignoringParenImpCasts(callExpr(
101 anyOf(hasType(isInteger()), hasType(hasCanonicalType(enumType()))),
102 unless(isInTemplateInstantiation())));
103 const auto SizeOfExpr = sizeOfExpr(hasArgumentOfType(
104 hasUnqualifiedDesugaredType(type().bind(
"sizeof-arg-type"))));
105 const auto SizeOfZero =
106 sizeOfExpr(has(ignoringParenImpCasts(integerLiteral(equals(0)))));
111 if (WarnOnSizeOfConstant) {
113 expr(sizeOfExpr(has(ignoringParenImpCasts(ConstantExpr))),
115 .bind(
"sizeof-constant"),
120 if (WarnOnSizeOfIntegerExpression) {
121 Finder->addMatcher(sizeOfExpr(ignoringParenImpCasts(has(IntegerCallExpr)))
122 .bind(
"sizeof-integer-call"),
127 if (WarnOnSizeOfThis) {
128 Finder->addMatcher(sizeOfExpr(has(ignoringParenImpCasts(cxxThisExpr())))
129 .bind(
"sizeof-this"),
134 const auto CharPtrType = pointerType(pointee(isAnyCharacter()));
135 const auto ConstStrLiteralDecl =
136 varDecl(isDefinition(), hasType(hasCanonicalType(CharPtrType)),
137 hasInitializer(ignoringParenImpCasts(stringLiteral())));
138 const auto VarWithConstStrLiteralDecl = expr(
139 hasType(hasCanonicalType(CharPtrType)),
140 ignoringParenImpCasts(declRefExpr(hasDeclaration(ConstStrLiteralDecl))));
142 sizeOfExpr(has(ignoringParenImpCasts(VarWithConstStrLiteralDecl)))
143 .bind(
"sizeof-charp"),
161 if (WarnOnSizeOfPointerToAggregate || WarnOnSizeOfPointer) {
162 const auto ArrayExpr =
163 ignoringParenImpCasts(hasType(hasCanonicalType(arrayType())));
164 const auto ArrayCastExpr = expr(anyOf(
165 unaryOperator(hasUnaryOperand(ArrayExpr), unless(hasOperatorName(
"*"))),
166 binaryOperator(hasEitherOperand(ArrayExpr)),
167 castExpr(hasSourceExpression(ArrayExpr))));
168 const auto PointerToArrayExpr =
169 hasType(hasCanonicalType(pointerType(pointee(arrayType()))));
171 const auto PointerToStructType =
172 hasUnqualifiedDesugaredType(pointerType(pointee(recordType())));
173 const auto PointerToStructTypeWithBinding =
174 type(PointerToStructType).bind(
"struct-type");
175 const auto PointerToStructExpr =
176 expr(hasType(hasCanonicalType(PointerToStructType)));
178 const auto PointerToDetectedExpr =
180 ? expr(hasType(hasUnqualifiedDesugaredType(pointerType())))
181 : expr(anyOf(ArrayCastExpr, PointerToArrayExpr,
182 PointerToStructExpr));
184 const auto ZeroLiteral = ignoringParenImpCasts(integerLiteral(equals(0)));
185 const auto SubscriptExprWithZeroIndex =
186 arraySubscriptExpr(hasIndex(ZeroLiteral));
187 const auto DerefExpr =
188 ignoringParenImpCasts(unaryOperator(hasOperatorName(
"*")));
191 expr(sizeOfExpr(anyOf(has(ignoringParenImpCasts(
192 expr(PointerToDetectedExpr, unless(DerefExpr),
193 unless(SubscriptExprWithZeroIndex),
194 unless(VarWithConstStrLiteralDecl),
195 unless(cxxThisExpr())))),
196 has(PointerToStructTypeWithBinding))))
197 .bind(
"sizeof-pointer"),
202 if (WarnOnSizeOfCompareToConstant) {
204 binaryOperator(matchers::isRelationalOperator(),
205 hasOperands(ignoringParenImpCasts(SizeOfExpr),
206 ignoringParenImpCasts(integerLiteral(anyOf(
207 equals(0), isBiggerThan(0x80000))))))
208 .bind(
"sizeof-compare-constant"),
215 has(ignoringParenImpCasts(
216 binaryOperator(hasOperatorName(
",")).bind(
"sizeof-comma-binop"))))
217 .bind(
"sizeof-comma-expr"),
227 const auto ElemType =
228 arrayType(hasElementType(recordType().bind(
"elem-type")));
229 const auto ElemPtrType = pointerType(pointee(type().bind(
"elem-ptr-type")));
230 const auto SizeofDivideExpr = binaryOperator(
231 hasOperatorName(
"/"),
233 ignoringParenImpCasts(sizeOfExpr(hasArgumentOfType(hasCanonicalType(
234 type(anyOf(ElemType, ElemPtrType, type())).bind(
"num-type")))))),
235 hasRHS(ignoringParenImpCasts(sizeOfExpr(
236 hasArgumentOfType(hasCanonicalType(type().bind(
"denom-type")))))));
238 Finder->addMatcher(SizeofDivideExpr.bind(
"sizeof-divide-expr"),
this);
241 Finder->addMatcher(binaryOperator(hasOperatorName(
"*"),
242 hasLHS(ignoringParenImpCasts(SizeOfExpr)),
243 hasRHS(ignoringParenImpCasts(SizeOfExpr)))
244 .bind(
"sizeof-multiply-sizeof"),
248 binaryOperator(hasOperatorName(
"*"),
249 hasOperands(ignoringParenImpCasts(SizeOfExpr),
250 ignoringParenImpCasts(binaryOperator(
251 hasOperatorName(
"*"),
253 ignoringParenImpCasts(SizeOfExpr))))))
254 .bind(
"sizeof-multiply-sizeof"),
259 Finder->addMatcher(sizeOfExpr(has(ignoringParenImpCasts(hasSizeOfDescendant(
260 8, allOf(SizeOfExpr, unless(SizeOfZero))))))
261 .bind(
"sizeof-sizeof-expr"),
267 const auto PtrDiffExpr = binaryOperator(
268 hasOperatorName(
"-"),
269 hasLHS(hasType(hasUnqualifiedDesugaredType(pointerType(pointee(
270 hasUnqualifiedDesugaredType(type().bind(
"left-ptr-type"))))))),
271 hasRHS(hasType(hasUnqualifiedDesugaredType(pointerType(pointee(
272 hasUnqualifiedDesugaredType(type().bind(
"right-ptr-type"))))))));
276 hasAnyOperatorName(
"==",
"!=",
"<",
"<=",
">",
">=",
"+",
"-"),
277 hasOperands(anyOf(ignoringParenImpCasts(
278 SizeOfExpr.bind(
"sizeof-ptr-mul-expr")),
279 ignoringParenImpCasts(binaryOperator(
280 hasOperatorName(
"*"),
281 hasEitherOperand(ignoringParenImpCasts(
282 SizeOfExpr.bind(
"sizeof-ptr-mul-expr")))))),
283 ignoringParenImpCasts(PtrDiffExpr)))
284 .bind(
"sizeof-in-ptr-arithmetic-mul"),
289 hasOperatorName(
"/"), hasLHS(ignoringParenImpCasts(PtrDiffExpr)),
290 hasRHS(ignoringParenImpCasts(SizeOfExpr.bind(
"sizeof-ptr-div-expr"))))
291 .bind(
"sizeof-in-ptr-arithmetic-div"),
306 const auto InterestingPtrTyForPtrArithmetic =
307 pointerType(pointee(qualType().bind(
"pointee-type")));
308 const auto SizeofLikeScaleExpr =
309 expr(anyOf(unaryExprOrTypeTraitExpr(ofKind(UETT_SizeOf)),
310 unaryExprOrTypeTraitExpr(ofKind(UETT_AlignOf)),
312 .bind(
"sizeof-in-ptr-arithmetic-scale-expr");
313 const auto PtrArithmeticIntegerScaleExpr = binaryOperator(
314 WarnOnOffsetDividedBySizeOf ? binaryOperator(hasAnyOperatorName(
"*",
"/"))
315 : binaryOperator(hasOperatorName(
"*")),
318 hasOperands(expr(hasType(isInteger()), unless(SizeofLikeScaleExpr)),
319 SizeofLikeScaleExpr));
320 const auto PtrArithmeticScaledIntegerExpr =
321 expr(anyOf(SizeofLikeScaleExpr, PtrArithmeticIntegerScaleExpr),
322 unless(SizeofDivideExpr));
326 binaryOperator(hasAnyOperatorName(
"+",
"-"),
327 hasOperands(hasType(InterestingPtrTyForPtrArithmetic),
328 PtrArithmeticScaledIntegerExpr))
329 .bind(
"sizeof-in-ptr-arithmetic-plusminus"),
330 binaryOperator(hasAnyOperatorName(
"+=",
"-="),
331 hasLHS(hasType(InterestingPtrTyForPtrArithmetic)),
332 hasRHS(PtrArithmeticScaledIntegerExpr))
333 .bind(
"sizeof-in-ptr-arithmetic-plusminus"))),
338 const ASTContext &Ctx = *Result.Context;
340 if (
const auto *
E = Result.Nodes.getNodeAs<Expr>(
"sizeof-constant")) {
341 diag(
E->getBeginLoc(),
"suspicious usage of 'sizeof(K)'; did you mean 'K'?")
342 <<
E->getSourceRange();
343 }
else if (
const auto *
E =
344 Result.Nodes.getNodeAs<Expr>(
"sizeof-integer-call")) {
345 diag(
E->getBeginLoc(),
"suspicious usage of 'sizeof()' on an expression "
347 <<
E->getSourceRange();
348 }
else if (
const auto *
E = Result.Nodes.getNodeAs<Expr>(
"sizeof-this")) {
349 diag(
E->getBeginLoc(),
350 "suspicious usage of 'sizeof(this)'; did you mean 'sizeof(*this)'")
351 <<
E->getSourceRange();
352 }
else if (
const auto *
E = Result.Nodes.getNodeAs<Expr>(
"sizeof-charp")) {
353 diag(
E->getBeginLoc(),
354 "suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?")
355 <<
E->getSourceRange();
356 }
else if (
const auto *
E = Result.Nodes.getNodeAs<Expr>(
"sizeof-pointer")) {
357 if (Result.Nodes.getNodeAs<
Type>(
"struct-type")) {
358 diag(
E->getBeginLoc(),
359 "suspicious usage of 'sizeof(A*)' on pointer-to-aggregate type; did "
360 "you mean 'sizeof(A)'?")
361 <<
E->getSourceRange();
363 diag(
E->getBeginLoc(),
"suspicious usage of 'sizeof()' on an expression "
365 <<
E->getSourceRange();
367 }
else if (
const auto *
E = Result.Nodes.getNodeAs<BinaryOperator>(
368 "sizeof-compare-constant")) {
369 diag(
E->getOperatorLoc(),
370 "suspicious comparison of 'sizeof(expr)' to a constant")
371 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
372 }
else if (
const auto *
E =
373 Result.Nodes.getNodeAs<Expr>(
"sizeof-comma-expr")) {
375 Result.Nodes.getNodeAs<BinaryOperator>(
"sizeof-comma-binop");
377 diag(BO->getOperatorLoc(),
"suspicious usage of 'sizeof(..., ...)'")
378 <<
E->getSourceRange();
379 }
else if (
const auto *
E =
380 Result.Nodes.getNodeAs<BinaryOperator>(
"sizeof-divide-expr")) {
381 const auto *NumTy = Result.Nodes.getNodeAs<
Type>(
"num-type");
382 const auto *DenomTy = Result.Nodes.getNodeAs<
Type>(
"denom-type");
383 const auto *ElementTy = Result.Nodes.getNodeAs<
Type>(
"elem-type");
384 const auto *PointedTy = Result.Nodes.getNodeAs<
Type>(
"elem-ptr-type");
386 CharUnits NumeratorSize = getSizeOfType(Ctx, NumTy);
387 CharUnits DenominatorSize = getSizeOfType(Ctx, DenomTy);
388 CharUnits ElementSize = getSizeOfType(Ctx, ElementTy);
390 if (DenominatorSize > CharUnits::Zero() &&
391 !NumeratorSize.isMultipleOf(DenominatorSize)) {
392 diag(
E->getOperatorLoc(),
"suspicious usage of 'sizeof(...)/sizeof(...)';"
393 " numerator is not a multiple of denominator")
394 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
395 }
else if (ElementSize > CharUnits::Zero() &&
396 DenominatorSize > CharUnits::Zero() &&
397 ElementSize != DenominatorSize) {
399 diag(
E->getOperatorLoc(),
400 "suspicious usage of 'sizeof(array)/sizeof(...)';"
401 " denominator differs from the size of array elements")
402 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
403 }
else if (NumTy && DenomTy && NumTy == DenomTy &&
404 !NumTy->isDependentType()) {
406 diag(
E->getOperatorLoc(),
407 "suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions "
408 "have the same type")
409 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
410 }
else if (!WarnOnSizeOfPointer) {
412 if (PointedTy && DenomTy && PointedTy == DenomTy) {
413 diag(
E->getOperatorLoc(),
414 "suspicious usage of 'sizeof(...)/sizeof(...)'; size of pointer "
415 "is divided by size of pointed type")
416 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
417 }
else if (NumTy && DenomTy && NumTy->isPointerType() &&
418 DenomTy->isPointerType()) {
419 diag(
E->getOperatorLoc(),
420 "suspicious usage of 'sizeof(...)/sizeof(...)'; both expressions "
421 "have pointer types")
422 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
425 }
else if (
const auto *
E =
426 Result.Nodes.getNodeAs<Expr>(
"sizeof-sizeof-expr")) {
427 diag(
E->getBeginLoc(),
"suspicious usage of 'sizeof(sizeof(...))'")
428 <<
E->getSourceRange();
429 }
else if (
const auto *
E = Result.Nodes.getNodeAs<BinaryOperator>(
430 "sizeof-multiply-sizeof")) {
431 diag(
E->getOperatorLoc(),
"suspicious 'sizeof' by 'sizeof' multiplication")
432 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
433 }
else if (
const auto *
E = Result.Nodes.getNodeAs<BinaryOperator>(
434 "sizeof-in-ptr-arithmetic-mul")) {
435 const auto *LPtrTy = Result.Nodes.getNodeAs<
Type>(
"left-ptr-type");
436 const auto *RPtrTy = Result.Nodes.getNodeAs<
Type>(
"right-ptr-type");
437 const auto *SizeofArgTy = Result.Nodes.getNodeAs<
Type>(
"sizeof-arg-type");
438 const auto *SizeOfExpr =
439 Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>(
"sizeof-ptr-mul-expr");
441 if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) {
442 diag(SizeOfExpr->getBeginLoc(),
"suspicious usage of 'sizeof(...)' in "
443 "pointer arithmetic")
444 << SizeOfExpr->getSourceRange() <<
E->getOperatorLoc()
445 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
447 }
else if (
const auto *
E = Result.Nodes.getNodeAs<BinaryOperator>(
448 "sizeof-in-ptr-arithmetic-div")) {
449 const auto *LPtrTy = Result.Nodes.getNodeAs<
Type>(
"left-ptr-type");
450 const auto *RPtrTy = Result.Nodes.getNodeAs<
Type>(
"right-ptr-type");
451 const auto *SizeofArgTy = Result.Nodes.getNodeAs<
Type>(
"sizeof-arg-type");
452 const auto *SizeOfExpr =
453 Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>(
"sizeof-ptr-div-expr");
455 if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) {
456 diag(SizeOfExpr->getBeginLoc(),
"suspicious usage of 'sizeof(...)' in "
457 "pointer arithmetic")
458 << SizeOfExpr->getSourceRange() <<
E->getOperatorLoc()
459 <<
E->getLHS()->getSourceRange() <<
E->getRHS()->getSourceRange();
461 }
else if (
const auto *
E = Result.Nodes.getNodeAs<BinaryOperator>(
462 "sizeof-in-ptr-arithmetic-plusminus")) {
463 const auto *PointeeTy = Result.Nodes.getNodeAs<QualType>(
"pointee-type");
464 const auto *ScaleExpr =
465 Result.Nodes.getNodeAs<Expr>(
"sizeof-in-ptr-arithmetic-scale-expr");
466 const CharUnits PointeeSize = getSizeOfType(Ctx, PointeeTy->getTypePtr());
467 const int ScaleKind = [ScaleExpr]() {
468 if (
const auto *UTTE = dyn_cast<UnaryExprOrTypeTraitExpr>(ScaleExpr))
469 switch (UTTE->getKind()) {
478 if (isa<OffsetOfExpr>(ScaleExpr))
484 if (ScaleKind != -1 && PointeeSize > CharUnits::One()) {
485 diag(
E->getExprLoc(),
486 "suspicious usage of '%select{sizeof|alignof|offsetof}0(...)' in "
487 "pointer arithmetic; this scaled value will be scaled again by the "
489 << ScaleKind <<
E->getOpcodeStr() << ScaleExpr->getSourceRange();
490 diag(
E->getExprLoc(),
491 "'%0' in pointer arithmetic internally scales with 'sizeof(%1)' == "
495 << PointeeTy->getAsString(Ctx.getPrintingPolicy())
496 << PointeeSize.getQuantity();
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.
AST_MATCHER(clang::VarDecl, hasConstantDeclaration)
llvm::StringMap< ClangTidyValue > OptionMap