clang-tools 6.0.0

ImplicitBoolConversionCheck.cpp
Go to the documentation of this file.
1 //===--- ImplicitBoolConversionCheck.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 "ImplicitBoolConversionCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
14 #include "clang/Tooling/FixIt.h"
15 #include <queue>
16
17 using namespace clang::ast_matchers;
18
19 namespace clang {
20 namespace tidy {
21 namespace readability {
22
23 namespace {
24
25 AST_MATCHER(Stmt, isMacroExpansion) {
26 SourceManager &SM = Finder->getASTContext().getSourceManager();
27 SourceLocation Loc = Node.getLocStart();
28 return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
29 }
30
31 bool isNULLMacroExpansion(const Stmt *Statement, ASTContext &Context) {
32 SourceManager &SM = Context.getSourceManager();
33 const LangOptions &LO = Context.getLangOpts();
34 SourceLocation Loc = Statement->getLocStart();
35 return SM.isMacroBodyExpansion(Loc) &&
36 Lexer::getImmediateMacroName(Loc, SM, LO) == "NULL";
37 }
38
39 AST_MATCHER(Stmt, isNULLMacroExpansion) {
40 return isNULLMacroExpansion(&Node, Finder->getASTContext());
41 }
42
43 StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
44 QualType Type,
45 ASTContext &Context) {
46 switch (CastExprKind) {
47 case CK_IntegralToBoolean:
48 return Type->isUnsignedIntegerType() ? "0u" : "0";
49
50 case CK_FloatingToBoolean:
51 return Context.hasSameType(Type, Context.FloatTy) ? "0.0f" : "0.0";
52
53 case CK_PointerToBoolean:
54 case CK_MemberPointerToBoolean: // Fall-through on purpose.
55 return Context.getLangOpts().CPlusPlus11 ? "nullptr" : "0";
56
57 default:
58 llvm_unreachable("Unexpected cast kind");
59 }
60 }
61
62 bool isUnaryLogicalNotOperator(const Stmt *Statement) {
63 const auto *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement);
64 return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;
65 }
66
67 bool areParensNeededForOverloadedOperator(OverloadedOperatorKind OperatorKind) {
68 switch (OperatorKind) {
69 case OO_New:
70 case OO_Delete: // Fall-through on purpose.
71 case OO_Array_New:
72 case OO_Array_Delete:
73 case OO_ArrowStar:
74 case OO_Arrow:
75 case OO_Call:
76 case OO_Subscript:
77 return false;
78
79 default:
80 return true;
81 }
82 }
83
84 bool areParensNeededForStatement(const Stmt *Statement) {
85 if (const auto *OperatorCall = dyn_cast<CXXOperatorCallExpr>(Statement)) {
86 return areParensNeededForOverloadedOperator(OperatorCall->getOperator());
87 }
88
89 return isa<BinaryOperator>(Statement) || isa<UnaryOperator>(Statement);
90 }
91
92 void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
93 const ImplicitCastExpr *Cast, const Stmt *Parent,
94 ASTContext &Context) {
95 // In case of expressions like (! integer), we should remove the redundant not
96 // operator and use inverted comparison (integer == 0).
97 bool InvertComparison =
98 Parent != nullptr && isUnaryLogicalNotOperator(Parent);
99 if (InvertComparison) {
100 SourceLocation ParentStartLoc = Parent->getLocStart();
101 SourceLocation ParentEndLoc =
102 cast<UnaryOperator>(Parent)->getSubExpr()->getLocStart();
103 Diag << FixItHint::CreateRemoval(
104 CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc));
105
106 Parent = Context.getParents(*Parent)[0].get<Stmt>();
107 }
108
109 const Expr *SubExpr = Cast->getSubExpr();
110
111 bool NeedInnerParens = areParensNeededForStatement(SubExpr);
112 bool NeedOuterParens =
113 Parent != nullptr && areParensNeededForStatement(Parent);
114
115 std::string StartLocInsertion;
116
117 if (NeedOuterParens) {
118 StartLocInsertion += "(";
119 }
120 if (NeedInnerParens) {
121 StartLocInsertion += "(";
122 }
123
124 if (!StartLocInsertion.empty()) {
125 Diag << FixItHint::CreateInsertion(Cast->getLocStart(), StartLocInsertion);
126 }
127
128 std::string EndLocInsertion;
129
130 if (NeedInnerParens) {
131 EndLocInsertion += ")";
132 }
133
134 if (InvertComparison) {
135 EndLocInsertion += " == ";
136 } else {
137 EndLocInsertion += " != ";
138 }
139
140 EndLocInsertion += getZeroLiteralToCompareWithForType(
141 Cast->getCastKind(), SubExpr->getType(), Context);
142
143 if (NeedOuterParens) {
144 EndLocInsertion += ")";
145 }
146
147 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
148 Cast->getLocEnd(), 0, Context.getSourceManager(), Context.getLangOpts());
149 Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);
150 }
151
152 StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression,
153 ASTContext &Context) {
154 if (isNULLMacroExpansion(Expression, Context)) {
155 return "false";
156 }
157
158 if (const auto *IntLit = dyn_cast<IntegerLiteral>(Expression)) {
159 return (IntLit->getValue() == 0) ? "false" : "true";
160 }
161
162 if (const auto *FloatLit = dyn_cast<FloatingLiteral>(Expression)) {
163 llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
164 FloatLitAbsValue.clearSign();
165 return (FloatLitAbsValue.bitcastToAPInt() == 0) ? "false" : "true";
166 }
167
168 if (const auto *CharLit = dyn_cast<CharacterLiteral>(Expression)) {
169 return (CharLit->getValue() == 0) ? "false" : "true";
170 }
171
172 if (isa<StringLiteral>(Expression->IgnoreCasts())) {
173 return "true";
174 }
175
176 return StringRef();
177 }
178
179 void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
180 const ImplicitCastExpr *Cast,
181 ASTContext &Context, StringRef OtherType) {
182 const Expr *SubExpr = Cast->getSubExpr();
183 bool NeedParens = !isa<ParenExpr>(SubExpr);
184
185 Diag << FixItHint::CreateInsertion(
186 Cast->getLocStart(),
187 (Twine("static_cast<") + OtherType + ">" + (NeedParens ? "(" : ""))
188 .str());
189
190 if (NeedParens) {
191 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
192 Cast->getLocEnd(), 0, Context.getSourceManager(),
193 Context.getLangOpts());
194
195 Diag << FixItHint::CreateInsertion(EndLoc, ")");
196 }
197 }
198
199 StringRef getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral,
200 QualType DestType, ASTContext &Context) {
201 // Prior to C++11, false literal could be implicitly converted to pointer.
202 if (!Context.getLangOpts().CPlusPlus11 &&
203 (DestType->isPointerType() || DestType->isMemberPointerType()) &&
204 BoolLiteral->getValue() == false) {
205 return "0";
206 }
207
208 if (DestType->isFloatingType()) {
209 if (Context.hasSameType(DestType, Context.FloatTy)) {
210 return BoolLiteral->getValue() ? "1.0f" : "0.0f";
211 }
212 return BoolLiteral->getValue() ? "1.0" : "0.0";
213 }
214
215 if (DestType->isUnsignedIntegerType()) {
216 return BoolLiteral->getValue() ? "1u" : "0u";
217 }
218 return BoolLiteral->getValue() ? "1" : "0";
219 }
220
221 bool isCastAllowedInCondition(const ImplicitCastExpr *Cast,
222 ASTContext &Context) {
223 std::queue<const Stmt *> Q;
224 Q.push(Cast);
225 while (!Q.empty()) {
226 for (const auto &N : Context.getParents(*Q.front())) {
227 const Stmt *S = N.get<Stmt>();
228 if (!S)
229 return false;
230 if (isa<IfStmt>(S) || isa<ConditionalOperator>(S) || isa<ForStmt>(S) ||
231 isa<WhileStmt>(S) || isa<BinaryConditionalOperator>(S))
232 return true;
233 if (isa<ParenExpr>(S) || isa<ImplicitCastExpr>(S) ||
234 isUnaryLogicalNotOperator(S) ||
235 (isa<BinaryOperator>(S) && cast<BinaryOperator>(S)->isLogicalOp())) {
236 Q.push(S);
237 } else {
238 return false;
239 }
240 }
241 Q.pop();
242 }
243 return false;
244 }
245
246 } // anonymous namespace
247
248 ImplicitBoolConversionCheck::ImplicitBoolConversionCheck(
249 StringRef Name, ClangTidyContext *Context)
250 : ClangTidyCheck(Name, Context),
251 AllowIntegerConditions(Options.get("AllowIntegerConditions", false)),
252 AllowPointerConditions(Options.get("AllowPointerConditions", false)) {}
253
254 void ImplicitBoolConversionCheck::storeOptions(
255 ClangTidyOptions::OptionMap &Opts) {
256 Options.store(Opts, "AllowIntegerConditions", AllowIntegerConditions);
257 Options.store(Opts, "AllowPointerConditions", AllowPointerConditions);
258 }
259
260 void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
261 // This check doesn't make much sense if we run it on language without
262 // built-in bool support.
263 if (!getLangOpts().Bool) {
264 return;
265 }
266
267 auto exceptionCases =
268 expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
269 hasParent(explicitCastExpr())));
270 auto implicitCastFromBool = implicitCastExpr(
271 anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
272 // Prior to C++11 cast from bool literal to pointer was allowed.
273 allOf(anyOf(hasCastKind(CK_NullToPointer),
274 hasCastKind(CK_NullToMemberPointer)),
275 hasSourceExpression(cxxBoolLiteral()))),
276 hasSourceExpression(expr(hasType(booleanType()))),
277 unless(exceptionCases));
278 auto boolXor =
279 binaryOperator(hasOperatorName("^"), hasLHS(implicitCastFromBool),
280 hasRHS(implicitCastFromBool));
281 Finder->addMatcher(
282 implicitCastExpr(
283 anyOf(hasCastKind(CK_IntegralToBoolean),
284 hasCastKind(CK_FloatingToBoolean),
285 hasCastKind(CK_PointerToBoolean),
286 hasCastKind(CK_MemberPointerToBoolean)),
287 // Exclude case of using if or while statements with variable
288 // declaration, e.g.:
289 // if (int var = functionCall()) {}
290 unless(
291 hasParent(stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
292 // Exclude cases common to implicit cast to and from bool.
293 unless(exceptionCases), unless(has(boolXor)),
294 // Retrive also parent statement, to check if we need additional
295 // parens in replacement.
296 anyOf(hasParent(stmt().bind("parentStmt")), anything()),
297 unless(isInTemplateInstantiation()),
298 unless(hasAncestor(functionTemplateDecl())))
299 .bind("implicitCastToBool"),
300 this);
301
302 auto boolComparison = binaryOperator(
303 anyOf(hasOperatorName("=="), hasOperatorName("!=")),
304 hasLHS(implicitCastFromBool), hasRHS(implicitCastFromBool));
305 auto boolOpAssignment =
306 binaryOperator(anyOf(hasOperatorName("|="), hasOperatorName("&=")),
307 hasLHS(expr(hasType(booleanType()))));
308 Finder->addMatcher(
309 implicitCastExpr(
310 implicitCastFromBool,
311 // Exclude comparisons of bools, as they are always cast to integers
312 // in such context:
313 // bool_expr_a == bool_expr_b
314 // bool_expr_a != bool_expr_b
315 unless(hasParent(binaryOperator(
316 anyOf(boolComparison, boolXor, boolOpAssignment)))),
317 // Check also for nested casts, for example: bool -> int -> float.
318 anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
319 anything()),
320 unless(isInTemplateInstantiation()),
321 unless(hasAncestor(functionTemplateDecl())))
322 .bind("implicitCastFromBool"),
323 this);
324 }
325
326 void ImplicitBoolConversionCheck::check(
327 const MatchFinder::MatchResult &Result) {
328 if (const auto *CastToBool =
329 Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) {
330 const auto *Parent = Result.Nodes.getNodeAs<Stmt>("parentStmt");
331 return handleCastToBool(CastToBool, Parent, *Result.Context);
332 }
333
334 if (const auto *CastFromBool =
335 Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastFromBool")) {
336 const auto *NextImplicitCast =
337 Result.Nodes.getNodeAs<ImplicitCastExpr>("furtherImplicitCast");
338 return handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context);
339 }
340 }
341
342 void ImplicitBoolConversionCheck::handleCastToBool(const ImplicitCastExpr *Cast,
343 const Stmt *Parent,
344 ASTContext &Context) {
345 if (AllowPointerConditions &&
346 (Cast->getCastKind() == CK_PointerToBoolean ||
347 Cast->getCastKind() == CK_MemberPointerToBoolean) &&
348 isCastAllowedInCondition(Cast, Context)) {
349 return;
350 }
351
352 if (AllowIntegerConditions && Cast->getCastKind() == CK_IntegralToBoolean &&
353 isCastAllowedInCondition(Cast, Context)) {
354 return;
355 }
356
357 auto Diag = diag(Cast->getLocStart(), "implicit conversion %0 -> bool")
358 << Cast->getSubExpr()->getType();
359
360 StringRef EquivalentLiteral =
361 getEquivalentBoolLiteralForExpr(Cast->getSubExpr(), Context);
362 if (!EquivalentLiteral.empty()) {
363 Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral);
364 } else {
365 fixGenericExprCastToBool(Diag, Cast, Parent, Context);
366 }
367 }
368
369 void ImplicitBoolConversionCheck::handleCastFromBool(
370 const ImplicitCastExpr *Cast, const ImplicitCastExpr *NextImplicitCast,
371 ASTContext &Context) {
372 QualType DestType =
373 NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
374 auto Diag = diag(Cast->getLocStart(), "implicit conversion bool -> %0")
375 << DestType;
376
377 if (const auto *BoolLiteral =
378 dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr())) {
379 Diag << tooling::fixit::createReplacement(
380 *Cast, getEquivalentForBoolLiteral(BoolLiteral, DestType, Context));
381 } else {
382 fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString());
383 }
384 }
385
386 } // namespace readability
387 } // namespace tidy
388 } // namespace clang
Loc
SourceLocation Loc
&#39;#&#39; location in the include directive
Definition: IncludeOrderCheck.cpp:36
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::getLangOpts
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
clang::tidy::readability::ImplicitBoolConversionCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: ImplicitBoolConversionCheck.cpp:260
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
clang::tidy::readability::ImplicitBoolConversionCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: ImplicitBoolConversionCheck.cpp:326
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
ImplicitBoolConversionCheck.h
clang
Definition: AndroidTidyModule.cpp:28
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:104
clang::tidy::readability::ImplicitBoolConversionCheck::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: ImplicitBoolConversionCheck.cpp:254
clang::ast_matchers::AST_MATCHER
AST_MATCHER(VarDecl, isAsm)
Definition: NoAssemblerCheck.cpp:18
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