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