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