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); 224 225 TraversalKindScope RAII(Context, ast_type_traits::TK_AsIs); 226 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) { 263 auto exceptionCases = 264 expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())), 265 has(ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1)))))), 266 hasParent(explicitCastExpr()))); 267 auto implicitCastFromBool = implicitCastExpr( 268 anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating), 269 // Prior to C++11 cast from bool literal to pointer was allowed. 270 allOf(anyOf(hasCastKind(CK_NullToPointer), 271 hasCastKind(CK_NullToMemberPointer)), 272 hasSourceExpression(cxxBoolLiteral()))), 273 hasSourceExpression(expr(hasType(booleanType()))), 274 unless(exceptionCases)); 275 auto boolXor = 276 binaryOperator(hasOperatorName("^"), hasLHS(implicitCastFromBool), 277 hasRHS(implicitCastFromBool)); 278 Finder->addMatcher( 279 traverse(ast_type_traits::TK_AsIs, 280 implicitCastExpr( 281 anyOf(hasCastKind(CK_IntegralToBoolean), 282 hasCastKind(CK_FloatingToBoolean), 283 hasCastKind(CK_PointerToBoolean), 284 hasCastKind(CK_MemberPointerToBoolean)), 285 // Exclude case of using if or while statements with variable 286 // declaration, e.g.: 287 // if (int var = functionCall()) {} 288 unless(hasParent( 289 stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))), 290 // Exclude cases common to implicit cast to and from bool. 291 unless(exceptionCases), unless(has(boolXor)), 292 // Retrieve also parent statement, to check if we need 293 // additional parens in replacement. 294 anyOf(hasParent(stmt().bind("parentStmt")), anything()), 295 unless(isInTemplateInstantiation()), 296 unless(hasAncestor(functionTemplateDecl()))) 297 .bind("implicitCastToBool")), 298 this); 299 300 auto boolComparison = binaryOperator(hasAnyOperatorName("==", "!="), 301 hasLHS(implicitCastFromBool), 302 hasRHS(implicitCastFromBool)); 303 auto boolOpAssignment = binaryOperator(hasAnyOperatorName("|=", "&="), 304 hasLHS(expr(hasType(booleanType())))); 305 auto bitfieldAssignment = binaryOperator( 306 hasLHS(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1)))))); 307 auto bitfieldConstruct = cxxConstructorDecl(hasDescendant(cxxCtorInitializer( 308 withInitializer(equalsBoundNode("implicitCastFromBool")), 309 forField(hasBitWidth(1))))); 310 Finder->addMatcher( 311 traverse( 312 ast_type_traits::TK_AsIs, 313 implicitCastExpr( 314 implicitCastFromBool, 315 // Exclude comparisons of bools, as they are always cast to 316 // integers in such context: 317 // bool_expr_a == bool_expr_b 318 // bool_expr_a != bool_expr_b 319 unless(hasParent( 320 binaryOperator(anyOf(boolComparison, boolXor, 321 boolOpAssignment, bitfieldAssignment)))), 322 implicitCastExpr().bind("implicitCastFromBool"), 323 unless(hasParent(bitfieldConstruct)), 324 // Check also for nested casts, for example: bool -> int -> float. 325 anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")), 326 anything()), 327 unless(isInTemplateInstantiation()), 328 unless(hasAncestor(functionTemplateDecl())))), 329 this); 330 } 331 332 void ImplicitBoolConversionCheck::check( 333 const MatchFinder::MatchResult &Result) { 334 335 if (const auto *CastToBool = 336 Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) { 337 const auto *Parent = Result.Nodes.getNodeAs<Stmt>("parentStmt"); 338 return handleCastToBool(CastToBool, Parent, *Result.Context); 339 } 340 341 if (const auto *CastFromBool = 342 Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastFromBool")) { 343 const auto *NextImplicitCast = 344 Result.Nodes.getNodeAs<ImplicitCastExpr>("furtherImplicitCast"); 345 return handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context); 346 } 347 } 348 349 void ImplicitBoolConversionCheck::handleCastToBool(const ImplicitCastExpr *Cast, 350 const Stmt *Parent, 351 ASTContext &Context) { 352 if (AllowPointerConditions && 353 (Cast->getCastKind() == CK_PointerToBoolean || 354 Cast->getCastKind() == CK_MemberPointerToBoolean) && 355 isCastAllowedInCondition(Cast, Context)) { 356 return; 357 } 358 359 if (AllowIntegerConditions && Cast->getCastKind() == CK_IntegralToBoolean && 360 isCastAllowedInCondition(Cast, Context)) { 361 return; 362 } 363 364 auto Diag = diag(Cast->getBeginLoc(), "implicit conversion %0 -> bool") 365 << Cast->getSubExpr()->getType(); 366 367 StringRef EquivalentLiteral = 368 getEquivalentBoolLiteralForExpr(Cast->getSubExpr(), Context); 369 if (!EquivalentLiteral.empty()) { 370 Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral); 371 } else { 372 fixGenericExprCastToBool(Diag, Cast, Parent, Context); 373 } 374 } 375 376 void ImplicitBoolConversionCheck::handleCastFromBool( 377 const ImplicitCastExpr *Cast, const ImplicitCastExpr *NextImplicitCast, 378 ASTContext &Context) { 379 QualType DestType = 380 NextImplicitCast ? NextImplicitCast->getType() : Cast->getType(); 381 auto Diag = diag(Cast->getBeginLoc(), "implicit conversion bool -> %0") 382 << DestType; 383 384 if (const auto *BoolLiteral = 385 dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr())) { 386 Diag << tooling::fixit::createReplacement( 387 *Cast, getEquivalentForBoolLiteral(BoolLiteral, DestType, Context)); 388 } else { 389 fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString()); 390 } 391 } 392 393 } // namespace readability 394 } // namespace tidy 395 } // namespace clang 396