clang-tools 6.0.0

SizeofExpressionCheck.cpp
Go to the documentation of this file.
1 //===--- SizeofExpressionCheck.cpp - clang-tidy----------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "SizeofExpressionCheck.h"
11 #include "../utils/Matchers.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14
15 using namespace clang::ast_matchers;
16
17 namespace clang {
18 namespace tidy {
19 namespace misc {
20
21 namespace {
22
23 AST_MATCHER_P(IntegerLiteral, isBiggerThan, unsigned, N) {
24 return Node.getValue().getZExtValue() > N;
25 }
26
27 AST_MATCHER_P2(Expr, hasSizeOfDescendant, int, Depth,
28 ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
29 if (Depth < 0)
30 return false;
31
32 const Expr *E = Node.IgnoreParenImpCasts();
33 if (InnerMatcher.matches(*E, Finder, Builder))
34 return true;
35
36 if (const auto *CE = dyn_cast<CastExpr>(E)) {
37 const auto M = hasSizeOfDescendant(Depth - 1, InnerMatcher);
38 return M.matches(*CE->getSubExpr(), Finder, Builder);
39 } else if (const auto *UE = dyn_cast<UnaryOperator>(E)) {
40 const auto M = hasSizeOfDescendant(Depth - 1, InnerMatcher);
41 return M.matches(*UE->getSubExpr(), Finder, Builder);
42 } else if (const auto *BE = dyn_cast<BinaryOperator>(E)) {
43 const auto LHS = hasSizeOfDescendant(Depth - 1, InnerMatcher);
44 const auto RHS = hasSizeOfDescendant(Depth - 1, InnerMatcher);
45 return LHS.matches(*BE->getLHS(), Finder, Builder) ||
46 RHS.matches(*BE->getRHS(), Finder, Builder);
47 }
48
49 return false;
50 }
51
52 CharUnits getSizeOfType(const ASTContext &Ctx, const Type *Ty) {
53 if (!Ty || Ty->isIncompleteType() || Ty->isDependentType() ||
54 isa<DependentSizedArrayType>(Ty) || !Ty->isConstantSizeType())
55 return CharUnits::Zero();
56 return Ctx.getTypeSizeInChars(Ty);
57 }
58
59 } // namespace
60
61 SizeofExpressionCheck::SizeofExpressionCheck(StringRef Name,
62 ClangTidyContext *Context)
63 : ClangTidyCheck(Name, Context),
64 WarnOnSizeOfConstant(Options.get("WarnOnSizeOfConstant", 1) != 0),
65 WarnOnSizeOfThis(Options.get("WarnOnSizeOfThis", 1) != 0),
66 WarnOnSizeOfCompareToConstant(
67 Options.get("WarnOnSizeOfCompareToConstant", 1) != 0) {}
68
69 void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
70 Options.store(Opts, "WarnOnSizeOfConstant", WarnOnSizeOfConstant);
71 Options.store(Opts, "WarnOnSizeOfThis", WarnOnSizeOfThis);
72 Options.store(Opts, "WarnOnSizeOfCompareToConstant",
73 WarnOnSizeOfCompareToConstant);
74 }
75
76 void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
77 const auto IntegerExpr = ignoringParenImpCasts(integerLiteral());
78 const auto ConstantExpr = expr(ignoringParenImpCasts(
79 anyOf(integerLiteral(), unaryOperator(hasUnaryOperand(IntegerExpr)),
80 binaryOperator(hasLHS(IntegerExpr), hasRHS(IntegerExpr)))));
81 const auto SizeOfExpr =
82 expr(anyOf(sizeOfExpr(has(type())), sizeOfExpr(has(expr()))));
83 const auto SizeOfZero = expr(
84 sizeOfExpr(has(ignoringParenImpCasts(expr(integerLiteral(equals(0)))))));
85
86 // Detect expression like: sizeof(ARRAYLEN);
87 // Note: The expression 'sizeof(sizeof(0))' is a portable trick used to know
88 // the sizeof size_t.
89 if (WarnOnSizeOfConstant) {
90 Finder->addMatcher(
91 expr(sizeOfExpr(has(ignoringParenImpCasts(ConstantExpr))),
92 unless(SizeOfZero))
93 .bind("sizeof-constant"),
94 this);
95 }
96
97 // Detect expression like: sizeof(this);
98 if (WarnOnSizeOfThis) {
99 Finder->addMatcher(
100 expr(sizeOfExpr(has(ignoringParenImpCasts(expr(cxxThisExpr())))))
101 .bind("sizeof-this"),
102 this);
103 }
104
105 // Detect sizeof(kPtr) where kPtr is 'const char* kPtr = "abc"';
106 const auto CharPtrType = pointerType(pointee(isAnyCharacter()));
107 const auto ConstStrLiteralDecl =
108 varDecl(isDefinition(), hasType(qualType(hasCanonicalType(CharPtrType))),
109 hasInitializer(ignoringParenImpCasts(stringLiteral())));
110 Finder->addMatcher(expr(sizeOfExpr(has(ignoringParenImpCasts(expr(
111 hasType(qualType(hasCanonicalType(CharPtrType))),
112 ignoringParenImpCasts(declRefExpr(
113 hasDeclaration(ConstStrLiteralDecl))))))))
114 .bind("sizeof-charp"),
115 this);
116
117 // Detect sizeof(ptr) where ptr points to an aggregate (i.e. sizeof(&S)).
118 const auto ArrayExpr = expr(ignoringParenImpCasts(
119 expr(hasType(qualType(hasCanonicalType(arrayType()))))));
120 const auto ArrayCastExpr = expr(anyOf(
121 unaryOperator(hasUnaryOperand(ArrayExpr), unless(hasOperatorName("*"))),
122 binaryOperator(hasEitherOperand(ArrayExpr)),
123 castExpr(hasSourceExpression(ArrayExpr))));
124 const auto PointerToArrayExpr = expr(ignoringParenImpCasts(expr(
125 hasType(qualType(hasCanonicalType(pointerType(pointee(arrayType()))))))));
126
127 const auto StructAddrOfExpr =
128 unaryOperator(hasOperatorName("&"),
129 hasUnaryOperand(ignoringParenImpCasts(expr(
130 hasType(qualType(hasCanonicalType(recordType())))))));
131
132 Finder->addMatcher(
133 expr(sizeOfExpr(has(expr(ignoringParenImpCasts(
134 anyOf(ArrayCastExpr, PointerToArrayExpr, StructAddrOfExpr))))))
135 .bind("sizeof-pointer-to-aggregate"),
136 this);
137
138 // Detect expression like: sizeof(epxr) <= k for a suspicious constant 'k'.
139 if (WarnOnSizeOfCompareToConstant) {
140 Finder->addMatcher(
141 binaryOperator(matchers::isRelationalOperator(),
142 hasEitherOperand(ignoringParenImpCasts(SizeOfExpr)),
143 hasEitherOperand(ignoringParenImpCasts(
144 anyOf(integerLiteral(equals(0)),
145 integerLiteral(isBiggerThan(0x80000))))))
146 .bind("sizeof-compare-constant"),
147 this);
148 }
149
150 // Detect expression like: sizeof(expr, expr); most likely an error.
151 Finder->addMatcher(expr(sizeOfExpr(has(expr(ignoringParenImpCasts(
152 binaryOperator(hasOperatorName(",")))))))
153 .bind("sizeof-comma-expr"),
154 this);
155
156 // Detect sizeof(...) /sizeof(...));
157 const auto ElemType =
158 arrayType(hasElementType(recordType().bind("elem-type")));
159 const auto ElemPtrType = pointerType(pointee(type().bind("elem-ptr-type")));
160 const auto NumType = qualType(hasCanonicalType(
161 type(anyOf(ElemType, ElemPtrType, type())).bind("num-type")));
162 const auto DenomType = qualType(hasCanonicalType(type().bind("denom-type")));
163
164 Finder->addMatcher(
165 binaryOperator(hasOperatorName("/"),
166 hasLHS(expr(ignoringParenImpCasts(
167 anyOf(sizeOfExpr(has(NumType)),
168 sizeOfExpr(has(expr(hasType(NumType)))))))),
169 hasRHS(expr(ignoringParenImpCasts(
170 anyOf(sizeOfExpr(has(DenomType)),
171 sizeOfExpr(has(expr(hasType(DenomType)))))))))
172 .bind("sizeof-divide-expr"),
173 this);
174
175 // Detect expression like: sizeof(...) * sizeof(...)); most likely an error.
176 Finder->addMatcher(binaryOperator(hasOperatorName("*"),
177 hasLHS(ignoringParenImpCasts(SizeOfExpr)),
178 hasRHS(ignoringParenImpCasts(SizeOfExpr)))
179 .bind("sizeof-multiply-sizeof"),
180 this);
181
182 Finder->addMatcher(
183 binaryOperator(hasOperatorName("*"),
184 hasEitherOperand(ignoringParenImpCasts(SizeOfExpr)),
185 hasEitherOperand(ignoringParenImpCasts(binaryOperator(
186 hasOperatorName("*"),
187 hasEitherOperand(ignoringParenImpCasts(SizeOfExpr))))))
188 .bind("sizeof-multiply-sizeof"),
189 this);
190
191 // Detect strange double-sizeof expression like: sizeof(sizeof(...));
192 // Note: The expression 'sizeof(sizeof(0))' is accepted.
193 Finder->addMatcher(
194 expr(sizeOfExpr(has(ignoringParenImpCasts(expr(
195 hasSizeOfDescendant(8, expr(SizeOfExpr, unless(SizeOfZero))))))))
196 .bind("sizeof-sizeof-expr"),
197 this);
198 }
199
200 void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
201 const ASTContext &Ctx = *Result.Context;
202
203 if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-constant")) {
204 diag(E->getLocStart(),
205 "suspicious usage of 'sizeof(K)'; did you mean 'K'?");
206 } else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-this")) {
207 diag(E->getLocStart(),
208 "suspicious usage of 'sizeof(this)'; did you mean 'sizeof(*this)'");
209 } else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-charp")) {
210 diag(E->getLocStart(),
211 "suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?");
212 } else if (const auto *E =
213 Result.Nodes.getNodeAs<Expr>("sizeof-pointer-to-aggregate")) {
214 diag(E->getLocStart(),
215 "suspicious usage of 'sizeof(A*)'; pointer to aggregate");
216 } else if (const auto *E =
217 Result.Nodes.getNodeAs<Expr>("sizeof-compare-constant")) {
218 diag(E->getLocStart(),
219 "suspicious comparison of 'sizeof(expr)' to a constant");
220 } else if (const auto *E =
221 Result.Nodes.getNodeAs<Expr>("sizeof-comma-expr")) {
222 diag(E->getLocStart(), "suspicious usage of 'sizeof(..., ...)'");
223 } else if (const auto *E =
224 Result.Nodes.getNodeAs<Expr>("sizeof-divide-expr")) {
225 const auto *NumTy = Result.Nodes.getNodeAs<Type>("num-type");
226 const auto *DenomTy = Result.Nodes.getNodeAs<Type>("denom-type");
227 const auto *ElementTy = Result.Nodes.getNodeAs<Type>("elem-type");
228 const auto *PointedTy = Result.Nodes.getNodeAs<Type>("elem-ptr-type");
229
230 CharUnits NumeratorSize = getSizeOfType(Ctx, NumTy);
231 CharUnits DenominatorSize = getSizeOfType(Ctx, DenomTy);
232 CharUnits ElementSize = getSizeOfType(Ctx, ElementTy);
233
234 if (DenominatorSize > CharUnits::Zero() &&
235 !NumeratorSize.isMultipleOf(DenominatorSize)) {
236 diag(E->getLocStart(), "suspicious usage of 'sizeof(...)/sizeof(...)';"
237 " numerator is not a multiple of denominator");
238 } else if (ElementSize > CharUnits::Zero() &&
239 DenominatorSize > CharUnits::Zero() &&
240 ElementSize != DenominatorSize) {
241 diag(E->getLocStart(), "suspicious usage of 'sizeof(...)/sizeof(...)';"
242 " numerator is not a multiple of denominator");
243 } else if (NumTy && DenomTy && NumTy == DenomTy) {
244 diag(E->getLocStart(),
245 "suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'");
246 } else if (PointedTy && DenomTy && PointedTy == DenomTy) {
247 diag(E->getLocStart(),
248 "suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'");
249 } else if (NumTy && DenomTy && NumTy->isPointerType() &&
250 DenomTy->isPointerType()) {
251 diag(E->getLocStart(),
252 "suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'");
253 }
254 } else if (const auto *E =
255 Result.Nodes.getNodeAs<Expr>("sizeof-sizeof-expr")) {
256 diag(E->getLocStart(), "suspicious usage of 'sizeof(sizeof(...))'");
257 } else if (const auto *E =
258 Result.Nodes.getNodeAs<Expr>("sizeof-multiply-sizeof")) {
259 diag(E->getLocStart(), "suspicious 'sizeof' by 'sizeof' multiplication");
260 }
261 }
262
263 } // namespace misc
264 } // namespace tidy
265 } // namespace clang
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
clang::tidy::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: ClangTidy.cpp:449
Name
StringHandle Name
Definition: PreprocessorTracker.cpp:525
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
SizeofExpressionCheck.h
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidy.h:183
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:99
clang::tidy::misc::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:200
clang::tidy::misc::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:69
clang
Definition: AndroidTidyModule.cpp:28
clang::tidy::misc::SizeofExpressionCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: SizeofExpressionCheck.cpp:76
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:104
clang::tidy::bugprone::AST_MATCHER_P
AST_MATCHER_P(IntegerLiteral, isBiggerThan, unsigned, N)
Definition: StringConstructorCheck.cpp:21
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:416