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
389