clang-tools 10.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
9 #include "SizeofExpressionCheck.h"
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
70 void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
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 hasEitherOperand(ignoringParenImpCasts(SizeOfExpr)),
167 hasEitherOperand(ignoringParenImpCasts(
168 anyOf(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 hasEitherOperand(ignoringParenImpCasts(SizeOfExpr)),
209 hasEitherOperand(ignoringParenImpCasts(binaryOperator(
210 hasOperatorName("*"),
211 hasEitherOperand(ignoringParenImpCasts(SizeOfExpr))))))
212 .bind("sizeof-multiply-sizeof"),
213 this);
214
215 // Detect strange double-sizeof expression like: sizeof(sizeof(...));
216 // Note: The expression 'sizeof(sizeof(0))' is accepted.
217 Finder->addMatcher(
218 expr(sizeOfExpr(has(ignoringParenImpCasts(expr(
219 hasSizeOfDescendant(8, expr(SizeOfExpr, unless(SizeOfZero))))))))
220 .bind("sizeof-sizeof-expr"),
221 this);
222
223 // Detect sizeof in pointer aritmetic like: N * sizeof(S) == P1 - P2 or
224 // (P1 - P2) / sizeof(S) where P1 and P2 are pointers to type S.
225 const auto PtrDiffExpr = binaryOperator(
226 hasOperatorName("-"),
227 hasLHS(expr(hasType(hasUnqualifiedDesugaredType(pointerType(pointee(
228 hasUnqualifiedDesugaredType(type().bind("left-ptr-type")))))))),
229 hasRHS(expr(hasType(hasUnqualifiedDesugaredType(pointerType(pointee(
230 hasUnqualifiedDesugaredType(type().bind("right-ptr-type")))))))));
231
232 Finder->addMatcher(
233 binaryOperator(
234 anyOf(hasOperatorName("=="), hasOperatorName("!="),
235 hasOperatorName("<"), hasOperatorName("<="),
236 hasOperatorName(">"), hasOperatorName(">="),
237 hasOperatorName("+"), hasOperatorName("-")),
238 hasEitherOperand(expr(anyOf(
239 ignoringParenImpCasts(SizeOfExpr),
240 ignoringParenImpCasts(binaryOperator(
241 hasOperatorName("*"),
242 hasEitherOperand(ignoringParenImpCasts(SizeOfExpr))))))),
243 hasEitherOperand(ignoringParenImpCasts(PtrDiffExpr)))
244 .bind("sizeof-in-ptr-arithmetic-mul"),
245 this);
246
247 Finder->addMatcher(binaryOperator(hasOperatorName("/"),
248 hasLHS(ignoringParenImpCasts(PtrDiffExpr)),
249 hasRHS(ignoringParenImpCasts(SizeOfExpr)))
250 .bind("sizeof-in-ptr-arithmetic-div"),
251 this);
252 }
253
254 void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
255 const ASTContext &Ctx = *Result.Context;
256
257 if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-constant")) {
258 diag(E->getBeginLoc(),
259 "suspicious usage of 'sizeof(K)'; did you mean 'K'?");
260 } else if (const auto *E =
261 Result.Nodes.getNodeAs<Expr>("sizeof-integer-call")) {
262 diag(E->getBeginLoc(), "suspicious usage of 'sizeof()' on an expression "
263 "that results in an integer");
264 } else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-this")) {
265 diag(E->getBeginLoc(),
266 "suspicious usage of 'sizeof(this)'; did you mean 'sizeof(*this)'");
267 } else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-charp")) {
268 diag(E->getBeginLoc(),
269 "suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?");
270 } else if (const auto *E =
271 Result.Nodes.getNodeAs<Expr>("sizeof-pointer-to-aggregate")) {
272 diag(E->getBeginLoc(),
273 "suspicious usage of 'sizeof(A*)'; pointer to aggregate");
274 } else if (const auto *E =
275 Result.Nodes.getNodeAs<Expr>("sizeof-compare-constant")) {
276 diag(E->getBeginLoc(),
277 "suspicious comparison of 'sizeof(expr)' to a constant");
278 } else if (const auto *E =
279 Result.Nodes.getNodeAs<Expr>("sizeof-comma-expr")) {
280 diag(E->getBeginLoc(), "suspicious usage of 'sizeof(..., ...)'");
281 } else if (const auto *E =
282 Result.Nodes.getNodeAs<Expr>("sizeof-divide-expr")) {
283 const auto *NumTy = Result.Nodes.getNodeAs<Type>("num-type");
284 const auto *DenomTy = Result.Nodes.getNodeAs<Type>("denom-type");
285 const auto *ElementTy = Result.Nodes.getNodeAs<Type>("elem-type");
286 const auto *PointedTy = Result.Nodes.getNodeAs<Type>("elem-ptr-type");
287
288 CharUnits NumeratorSize = getSizeOfType(Ctx, NumTy);
289 CharUnits DenominatorSize = getSizeOfType(Ctx, DenomTy);
290 CharUnits ElementSize = getSizeOfType(Ctx, ElementTy);
291
292 if (DenominatorSize > CharUnits::Zero() &&
293 !NumeratorSize.isMultipleOf(DenominatorSize)) {
294 diag(E->getBeginLoc(), "suspicious usage of 'sizeof(...)/sizeof(...)';"
295 " numerator is not a multiple of denominator");
296 } else if (ElementSize > CharUnits::Zero() &&
297 DenominatorSize > CharUnits::Zero() &&
298 ElementSize != DenominatorSize) {
299 diag(E->getBeginLoc(), "suspicious usage of 'sizeof(...)/sizeof(...)';"
300 " numerator is not a multiple of denominator");
301 } else if (NumTy && DenomTy && NumTy == DenomTy) {
302 diag(E->getBeginLoc(),
303 "suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'");
304 } else if (PointedTy && DenomTy && PointedTy == DenomTy) {
305 diag(E->getBeginLoc(),
306 "suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'");
307 } else if (NumTy && DenomTy && NumTy->isPointerType() &&
308 DenomTy->isPointerType()) {
309 diag(E->getBeginLoc(),
310 "suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'");
311 }
312 } else if (const auto *E =
313 Result.Nodes.getNodeAs<Expr>("sizeof-sizeof-expr")) {
314 diag(E->getBeginLoc(), "suspicious usage of 'sizeof(sizeof(...))'");
315 } else if (const auto *E =
316 Result.Nodes.getNodeAs<Expr>("sizeof-multiply-sizeof")) {
317 diag(E->getBeginLoc(), "suspicious 'sizeof' by 'sizeof' multiplication");
318 } else if (const auto *E =
319 Result.Nodes.getNodeAs<Expr>("sizeof-in-ptr-arithmetic-mul")) {
320 const auto *LPtrTy = Result.Nodes.getNodeAs<Type>("left-ptr-type");
321 const auto *RPtrTy = Result.Nodes.getNodeAs<Type>("right-ptr-type");
322 const auto *SizeofArgTy = Result.Nodes.getNodeAs<Type>("sizeof-arg-type");
323
324 if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) {
325 diag(E->getBeginLoc(), "suspicious usage of 'sizeof(...)' in "
326 "pointer arithmetic");
327 }
328 } else if (const auto *E =
329 Result.Nodes.getNodeAs<Expr>("sizeof-in-ptr-arithmetic-div")) {
330 const auto *LPtrTy = Result.Nodes.getNodeAs<Type>("left-ptr-type");
331 const auto *RPtrTy = Result.Nodes.getNodeAs<Type>("right-ptr-type");
332 const auto *SizeofArgTy = Result.Nodes.getNodeAs<Type>("sizeof-arg-type");
333
334 if ((LPtrTy == RPtrTy) && (LPtrTy == SizeofArgTy)) {
335 diag(E->getBeginLoc(), "suspicious usage of 'sizeof(...)' in "
336 "pointer arithmetic");
337 }
338 }
339 }
340
341 } // namespace bugprone
342 } // namespace tidy
343 } // namespace clang
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:254
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:47
SizeofExpressionCheck.h
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:184
Ctx
Context Ctx
Definition: TUScheduler.cpp:237
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:58
clang::tidy::bugprone::SizeofExpressionCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: SizeofExpressionCheck.cpp:79
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:97
Builder
CodeCompletionBuilder Builder
Definition: CodeCompletionStringsTests.cpp:35
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:77
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:82
Type
NodeType Type
Definition: HTMLGenerator.cpp:73
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: ClangTidyCheck.cpp:21
clang::tidy::readability::AST_MATCHER_P
AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl, ast_matchers::internal::Matcher< CXXMethodDecl >, InnerMatcher)
Definition: ConvertMemberFunctionsToStatic.cpp:52