Index: clang-tidy/readability/CMakeLists.txt =================================================================== --- clang-tidy/readability/CMakeLists.txt +++ clang-tidy/readability/CMakeLists.txt @@ -6,6 +6,7 @@ ElseAfterReturnCheck.cpp FunctionSizeCheck.cpp IdentifierNamingCheck.cpp + ImplicitBoolCastCheck.cpp InconsistentDeclarationParameterNameCheck.cpp NamedParameterCheck.cpp NamespaceCommentCheck.cpp Index: clang-tidy/readability/ImplicitBoolCastCheck.h =================================================================== --- clang-tidy/readability/ImplicitBoolCastCheck.h +++ clang-tidy/readability/ImplicitBoolCastCheck.h @@ -0,0 +1,40 @@ +//===--- ImplicitBoolCastCheck.h - clang-tidy--------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CAST_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CAST_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { + +/// \brief Checks for use of implicit bool casts in expressions. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability-implicit-bool-cast.html +class ImplicitBoolCastCheck : public ClangTidyCheck { +public: + ImplicitBoolCastCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + void handleCastToBool(const ImplicitCastExpr *CastExpression, + const Stmt *ParentStatement, ASTContext &Context); + void handleCastFromBool(const ImplicitCastExpr *CastExpression, + const ImplicitCastExpr *FurtherImplicitCastExpression, + ASTContext &Context); +}; + +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CAST_H Index: clang-tidy/readability/ImplicitBoolCastCheck.cpp =================================================================== --- clang-tidy/readability/ImplicitBoolCastCheck.cpp +++ clang-tidy/readability/ImplicitBoolCastCheck.cpp @@ -0,0 +1,340 @@ +//===--- ImplicitBoolCastCheck.cpp - clang-tidy----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ImplicitBoolCastCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" + +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { + +namespace { + +AST_MATCHER_P(CastExpr, hasCastKind, CastKind, Kind) { + return Node.getCastKind() == Kind; +} + +AST_MATCHER_P(BinaryOperator, hasOpcode, BinaryOperatorKind, OperatorKind) { + return Node.getOpcode() == OperatorKind; +} + +AST_MATCHER(QualType, isBoolType) { + return not Node.isNull() and Node->isBooleanType(); +} + +AST_MATCHER(Expr, comesFromMacroExpansion) { + SourceManager &SM = Finder->getASTContext().getSourceManager(); + SourceLocation Loc = Node.getLocStart(); + return SM.isMacroBodyExpansion(Loc) or SM.isMacroArgExpansion(Loc); +} + +StatementMatcher createImplicitCastFromBoolMatcher() { + return implicitCastExpr( + unless(anyOf(hasParent(explicitCastExpr()), comesFromMacroExpansion(), + anyOf(hasAncestor(functionDecl( + ast_matchers::isTemplateInstantiation())), + hasAncestor(functionTemplateDecl())))), + anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating)), + hasSourceExpression( + anyOf(cxxBoolLiteral(), expr(hasType(qualType(isBoolType())))))); +} + +StringRef +getZeroLiteralToCompareWithForGivenType(CastKind CastExpressionKind, + QualType CastSubExpressionType, + ASTContext &Context) { + switch (CastExpressionKind) { + case CK_IntegralToBoolean: + return CastSubExpressionType->isUnsignedIntegerType() ? "0u" : "0"; + + case CK_FloatingToBoolean: + return Context.hasSameType(CastSubExpressionType, Context.FloatTy) ? "0.0f" + : "0.0"; + + case CK_PointerToBoolean: + case CK_MemberPointerToBoolean: // Fall-through on purpose. + return "nullptr"; + + default: + assert(false && "Unexpected cast kind"); + } +} + +bool isUnaryLogicalNotOperator(const Stmt *Statement) { + const auto *UnaryOperatorExpression = + llvm::dyn_cast_or_null(Statement); + return UnaryOperatorExpression != nullptr && + UnaryOperatorExpression->getOpcode() == UO_LNot; +} + +void addFixItHintsForGenericExpressionCastToBool( + DiagnosticBuilder &Diagnostic, const ImplicitCastExpr *CastExpression, + const Stmt *ParentStatement, ASTContext &Context) { + // In case of expressions like (! integer), we should remove the redundant not + // operator and use inverted comparison (integer == 0). + bool InvertComparison = isUnaryLogicalNotOperator(ParentStatement); + if (InvertComparison) { + SourceLocation ParentStartLoc = ParentStatement->getLocStart(); + SourceLocation ParentEndLoc = Lexer::getLocForEndOfToken( + ParentStatement->getLocStart(), -1, Context.getSourceManager(), + Context.getLangOpts()); + Diagnostic.AddFixItHint(FixItHint::CreateRemoval( + CharSourceRange::getTokenRange(ParentStartLoc, ParentEndLoc))); + + auto FurtherParents = Context.getParents(*ParentStatement); + ParentStatement = FurtherParents[0].get(); + } + + const Expr *SubExpression = CastExpression->getSubExpr(); + + bool NeedInnerParens = llvm::isa(SubExpression) or + llvm::isa(SubExpression); + bool NeedOuterParens = ParentStatement != nullptr and + (llvm::isa(ParentStatement) or + llvm::isa(ParentStatement)); + + SourceLocation StartLoc = CastExpression->getLocStart(); + SourceLocation EndLoc = Lexer::getLocForEndOfToken( + CastExpression->getLocEnd(), 0, Context.getSourceManager(), + Context.getLangOpts()); + + if (NeedOuterParens) { + Diagnostic.AddFixItHint(FixItHint::CreateInsertion(StartLoc, "(")); + } + if (NeedInnerParens) { + Diagnostic.AddFixItHint(FixItHint::CreateInsertion(StartLoc, "(")); + Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc, ")")); + } + + if (InvertComparison) { + Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc, " == ")); + } else { + Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc, " != ")); + } + + Diagnostic.AddFixItHint(FixItHint::CreateInsertion( + EndLoc, + getZeroLiteralToCompareWithForGivenType( + CastExpression->getCastKind(), SubExpression->getType(), Context))); + + if (NeedOuterParens) { + Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc, ")")); + } +} + +bool isLiteralExpression(const Expr *Expression) { + return llvm::isa(Expression) or + llvm::isa(Expression) or + llvm::isa(Expression) or + llvm::isa(Expression->IgnoreCasts()); +} + +StringRef +getEquivalentBoolLiteralForOtherLiteral(const Expr *LiteralExpression) { + const auto *IntegerLiteralExpression = + llvm::dyn_cast_or_null(LiteralExpression); + if (IntegerLiteralExpression != nullptr) { + return (IntegerLiteralExpression->getValue() == 0) ? "false" : "true"; + } + + const auto *FloatingLiteralExpression = + llvm::dyn_cast_or_null(LiteralExpression); + if (FloatingLiteralExpression != nullptr) { + return (FloatingLiteralExpression->getValue().bitcastToAPInt() == 0) + ? "false" + : "true"; + } + + const auto *CharacterLiteralExpression = + llvm::dyn_cast_or_null(LiteralExpression); + if (CharacterLiteralExpression != nullptr) { + return (CharacterLiteralExpression->getValue() == 0) ? "false" : "true"; + } + + const auto *StringLiteralExpression = + llvm::dyn_cast_or_null(LiteralExpression->IgnoreCasts()); + if (StringLiteralExpression != nullptr) { + return "true"; + } + + assert(false && "Unexpected literal expression"); +} + +void addFixItHintsForLiteralCastToBool(DiagnosticBuilder &Diagnostic, + const ImplicitCastExpr *CastExpression, + ASTContext &Context) { + SourceLocation StartLoc = CastExpression->getLocStart(); + SourceLocation EndLoc = Lexer::getLocForEndOfToken( + CastExpression->getLocEnd(), -1, Context.getSourceManager(), + Context.getLangOpts()); + + Diagnostic.AddFixItHint(FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(StartLoc, EndLoc), + getEquivalentBoolLiteralForOtherLiteral(CastExpression->getSubExpr()))); +} + +void addFixItHintsForGenericExpressionCastFromBool( + DiagnosticBuilder &Diagnostic, const ImplicitCastExpr *CastExpression, + ASTContext &Context, StringRef OtherType) { + const Expr *SubExpression = CastExpression->getSubExpr(); + bool NeedParens = not llvm::isa(SubExpression); + + SourceLocation StartLoc = CastExpression->getLocStart(); + SourceLocation EndLoc = Lexer::getLocForEndOfToken( + CastExpression->getLocEnd(), 0, Context.getSourceManager(), + Context.getLangOpts()); + + Diagnostic.AddFixItHint(FixItHint::CreateInsertion(StartLoc, "static_cast<")); + Diagnostic.AddFixItHint(FixItHint::CreateInsertion(StartLoc, OtherType)); + Diagnostic.AddFixItHint(FixItHint::CreateInsertion(StartLoc, ">")); + + if (NeedParens) { + Diagnostic.AddFixItHint(FixItHint::CreateInsertion(StartLoc, "(")); + Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc, ")")); + } +} + +StringRef getEquivalentNumericLiteralForBoolLiteral( + const CXXBoolLiteralExpr *BoolLiteralExpression, QualType DestinationType, + ASTContext &Context) { + if (DestinationType->isFloatingType()) { + if (BoolLiteralExpression->getValue() == true) { + return Context.hasSameType(DestinationType, Context.FloatTy) ? "1.0f" + : "1.0"; + } + return Context.hasSameType(DestinationType, Context.FloatTy) ? "0.0f" + : "0.0"; + } + + if (BoolLiteralExpression->getValue() == true) { + return DestinationType->isUnsignedIntegerType() ? "1u" : "1"; + } + return DestinationType->isUnsignedIntegerType() ? "0u" : "0"; +} + +void addFixItHintsForLiteralCastFromBool(DiagnosticBuilder &Diagnostic, + const ImplicitCastExpr *CastExpression, + ASTContext &Context, + QualType DestinationType) { + SourceLocation StartLoc = CastExpression->getLocStart(); + SourceLocation EndLoc = Lexer::getLocForEndOfToken( + CastExpression->getLocEnd(), -1, Context.getSourceManager(), + Context.getLangOpts()); + const auto *BoolLiteralExpression = + llvm::dyn_cast(CastExpression->getSubExpr()); + + Diagnostic.AddFixItHint(FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(StartLoc, EndLoc), + getEquivalentNumericLiteralForBoolLiteral(BoolLiteralExpression, + DestinationType, Context))); +} + +} // anonymous namespace + +void ImplicitBoolCastCheck::registerMatchers(MatchFinder *Finder) { + // This check doesn't make much sense if we run it on language without + // built-in bool support. + if (not getLangOpts().Bool) { + return; + } + + Finder->addMatcher( + implicitCastExpr( + unless(anyOf(hasParent(explicitCastExpr()), comesFromMacroExpansion(), + anyOf(hasAncestor(functionDecl( + ast_matchers::isTemplateInstantiation())), + hasAncestor(functionTemplateDecl())))), + anyOf(hasCastKind(CK_IntegralToBoolean), + hasCastKind(CK_FloatingToBoolean), + hasCastKind(CK_PointerToBoolean), + hasCastKind(CK_MemberPointerToBoolean)), + anyOf(hasParent(stmt().bind("parentStmt")), anything())) + .bind("implicitCastToBool"), + this); + + Finder->addMatcher( + implicitCastExpr( + createImplicitCastFromBoolMatcher(), + // Exclude comparisons of bools, as they are always cast to integers + // in such context: + // bool_expr_a == bool_expr_b + // bool_expr_a != bool_expr_b + unless(hasParent( + binaryOperator(anyOf(hasOpcode(BO_EQ), hasOpcode(BO_NE)), + hasLHS(createImplicitCastFromBoolMatcher()), + hasRHS(createImplicitCastFromBoolMatcher())))), + // Check also for nested casts, for example: bool -> int -> float. + anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")), + anything())) + .bind("implicitCastFromBool"), + this); +} + +void ImplicitBoolCastCheck::check(const MatchFinder::MatchResult &Result) { + const auto *CastToBool = + Result.Nodes.getNodeAs("implicitCastToBool"); + if (CastToBool != nullptr) { + const auto *ParentStatement = Result.Nodes.getNodeAs("parentStmt"); + return handleCastToBool(CastToBool, ParentStatement, *Result.Context); + } + + const auto *CastFromBool = + Result.Nodes.getNodeAs("implicitCastFromBool"); + if (CastFromBool != nullptr) { + const auto *FurtherImplicitCastExpression = + Result.Nodes.getNodeAs("furtherImplicitCast"); + return handleCastFromBool(CastFromBool, FurtherImplicitCastExpression, + *Result.Context); + } +} + +void ImplicitBoolCastCheck::handleCastToBool( + const ImplicitCastExpr *CastExpression, const Stmt *ParentStatement, + ASTContext &Context) { + std::string OtherType = CastExpression->getSubExpr()->getType().getAsString(); + DiagnosticBuilder Diagnostic = + diag(CastExpression->getLocStart(), "implicit cast '%0' -> bool") + << OtherType; + + if (isLiteralExpression(CastExpression->getSubExpr())) { + addFixItHintsForLiteralCastToBool(Diagnostic, CastExpression, Context); + } else { + addFixItHintsForGenericExpressionCastToBool(Diagnostic, CastExpression, + ParentStatement, Context); + } +} + +void ImplicitBoolCastCheck::handleCastFromBool( + const ImplicitCastExpr *CastExpression, + const ImplicitCastExpr *FurtherImplicitCastExpression, + ASTContext &Context) { + QualType DestinationType = (FurtherImplicitCastExpression != nullptr) + ? FurtherImplicitCastExpression->getType() + : CastExpression->getType(); + std::string DestinationTypeString = DestinationType.getAsString(); + DiagnosticBuilder Diagnostic = + diag(CastExpression->getLocStart(), "implicit cast bool -> '%0'") + << DestinationTypeString; + + if (llvm::isa(CastExpression->getSubExpr())) { + addFixItHintsForLiteralCastFromBool(Diagnostic, CastExpression, Context, + DestinationType); + } else { + addFixItHintsForGenericExpressionCastFromBool( + Diagnostic, CastExpression, Context, DestinationTypeString); + } +} + +} // namespace tidy +} // namespace clang Index: clang-tidy/readability/ReadabilityTidyModule.cpp =================================================================== --- clang-tidy/readability/ReadabilityTidyModule.cpp +++ clang-tidy/readability/ReadabilityTidyModule.cpp @@ -15,6 +15,7 @@ #include "ElseAfterReturnCheck.h" #include "FunctionSizeCheck.h" #include "IdentifierNamingCheck.h" +#include "ImplicitBoolCastCheck.h" #include "InconsistentDeclarationParameterNameCheck.h" #include "NamedParameterCheck.h" #include "RedundantSmartptrGetCheck.h" @@ -38,6 +39,8 @@ "readability-function-size"); CheckFactories.registerCheck( "readability-identifier-naming"); + CheckFactories.registerCheck( + "readability-implicit-bool-cast"); CheckFactories.registerCheck( "readability-inconsistent-declaration-parameter-name"); CheckFactories.registerCheck( Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -58,6 +58,7 @@ readability-else-after-return readability-function-size readability-identifier-naming + readability-implicit-bool-cast readability-inconsistent-declaration-parameter-name readability-named-parameter readability-redundant-smartptr-get Index: docs/clang-tidy/checks/readability-implicit-bool-cast.rst =================================================================== --- docs/clang-tidy/checks/readability-implicit-bool-cast.rst +++ docs/clang-tidy/checks/readability-implicit-bool-cast.rst @@ -0,0 +1,94 @@ +readability-implicit-bool-cast +============================== + +This check can be used to find implicit conversions between built-in types and +booleans. Depending on use case, it may simply help with readability of the code, +or in some cases, point to potential bugs which remain unnoticed due to implicit +conversions. + +The following is a real-world example of bug which was hiding behind implicit +bool cast: + +.. code:: c++ + + class Foo { + int m_foo; + public: + void setFoo(bool foo) { m_foo = foo; } // warning: implicit cast bool -> int + int getFoo() { return m_foo; } + }; + + void use(Foo& foo) { + bool value = foo.getFoo(); // warning: implicit cast int -> bool + } + +This code is the result of unsuccessful refactoring, where type of ``m_foo`` +changed from ``bool`` to ``int``. The programmer forgot to change all +occurences of ``bool``, and the remaining code is no longer correct, yet it +still compiles without any visible warnings. + +In addition to issuing warnings, FixIt hints are provided to help solve +the reported issues. This can be used for improving readabilty of code, for example: + +.. code:: c++ + + void conversionsToBool() { + float floating; + bool boolean = floating; + // ^ propose replacement: bool boolean = floating != 0.0f; + + int integer + if (integer) {} + // ^ propose replacement: if (integer != 0) {} + + int* pointer; + if (!pointer) {} + // ^ propose replacement: if (pointer == nullptr) {} + + while (1) {} + // ^ propose replacement: while (true) {} + } + + void functionTakingBool(bool param); + + void conversionsFromBool() { + int integer; + functionTakingBool(integer); + // ^ propose replacement: functionTakingBool(static_cast(integer)); + + functionTakingBool(1); + // ^ propose replacement: functionTakingBool(true); + } + +In general, the following cast types are checked: + + - integer expression/literal to boolean + - floating expression/literal to boolean + - pointer/pointer to member/``nullptr`` to boolean + - boolean expression/literal to integer + - boolean expression/literal to floating + +The rules for generating FixIt hints are: + - in case of casts from other built-in type to bool, an explicit comparison + is proposed to make it clear what exaclty is being compared: + + - ``bool boolean = floating;`` is changed to ``bool boolean = floating == 0.0f;`` + - for other types, appropriate literals are used (``0``, ``0u``, ``0.0f``, ``0.0``, ``nullptr``) + - in case of negated expressions cast to bool, the proposed replacement with + comparison is simplified: + + - ``if (!pointer)`` is changed to ``if (pointer == nullptr)`` + - in case of casts from bool to other built-in types, an explicit ``static_cast`` + is proposed to make it clear that a cast is taking place: + + - ``int integer = boolean;`` is changed to ``int integer = static_cast(boolean);`` + - if the cast is performed on type literals, an equivalent literal is proposed, + according to what type is actually expected, for example: + + - ``functionTakingBool(0);`` is changed to ``functionTakingBool(false);`` + - ``functionTakingInt(true);`` is changed to ``functionTakingInt(1);`` + - for other types, appropriate literals are used (``false``, ``true``, ``0``, ``1``, ``0u``, ``1u``, + ``0.0f``, ``1.0f``, ``0.0``, ``1.0f``) + +Occurences of implicit casts inside macros and template instantiations are +deliberately ignored, as it is not clear how to deal with such cases. \ No newline at end of file Index: test/clang-tidy/readability-implicit-bool-cast.cpp =================================================================== --- test/clang-tidy/readability-implicit-bool-cast.cpp +++ test/clang-tidy/readability-implicit-bool-cast.cpp @@ -0,0 +1,380 @@ +// RUN: %python %S/check_clang_tidy.py %s readability-implicit-bool-cast %t + +void functionTakingBool(bool); +void functionTakingInt(int); +void functionTakingUnsignedLong(unsigned long); +void functionTakingChar(char); +void functionTakingFloat(float); +void functionTakingDouble(double); + +struct Struct { + int member; +}; + + +////////// Implicit cast from bool. + +void implicitCastFromBoolSimpleCases() { + bool boolean = true; + + functionTakingBool(boolean); + + // CHECK-MESSAGES: :[[@LINE+2]]:21: warning: implicit cast bool -> 'int' + // CHECK-FIXES: functionTakingInt(static_cast(boolean)); + functionTakingInt(boolean); + + // CHECK-MESSAGES: :[[@LINE+2]]:30: warning: implicit cast bool -> 'unsigned long' + // CHECK-FIXES: functionTakingUnsignedLong(static_cast(boolean)); + functionTakingUnsignedLong(boolean); + + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast bool -> 'char' + // CHECK-FIXES: functionTakingChar(static_cast(boolean)); + functionTakingChar(boolean); + + // CHECK-MESSAGES: :[[@LINE+2]]:23: warning: implicit cast bool -> 'float' + // CHECK-FIXES: functionTakingFloat(static_cast(boolean)); + functionTakingFloat(boolean); + + // CHECK-MESSAGES: :[[@LINE+2]]:24: warning: implicit cast bool -> 'double' + // CHECK-FIXES: functionTakingDouble(static_cast(boolean)); + functionTakingDouble(boolean); +} + +float implicitCastFromBoolInReturnValue() { + bool boolean = false; + // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: implicit cast bool -> 'float' + // CHECK-FIXES: return static_cast(boolean); + return boolean; +} + +void implicitCastFromBoolInSingleBoolExpressions() { + bool boolean = true; + + // CHECK-MESSAGES: :[[@LINE+2]]:17: warning: implicit cast bool -> 'int' + // CHECK-FIXES: int integer = static_cast(boolean) - 3; + int integer = boolean - 3; + + // CHECK-MESSAGES: :[[@LINE+2]]:20: warning: implicit cast bool -> 'float' + // CHECK-FIXES: float floating = static_cast(boolean) / 0.3f; + float floating = boolean / 0.3f; + + // CHECK-MESSAGES: :[[@LINE+2]]:20: warning: implicit cast bool -> 'char' + // CHECK-FIXES: char character = static_cast(boolean); + char character = boolean; +} + +void implicitCastFromBoollInComplexBoolExpressions() { + bool boolean = true; + bool anotherBoolean = false; + + // CHECK-MESSAGES: :[[@LINE+2]]:17: warning: implicit cast bool -> 'int' + // CHECK-FIXES: int integer = static_cast(boolean && anotherBoolean); + int integer = boolean && anotherBoolean; + + // CHECK-MESSAGES: :[[@LINE+2]]:32: warning: implicit cast bool -> 'unsigned long' + // CHECK-FIXES: unsigned long unsignedLong = static_cast(! boolean) + 4ul; + unsigned long unsignedLong = (! boolean) + 4ul; + + // CHECK-MESSAGES: :[[@LINE+2]]:20: warning: implicit cast bool -> 'float' + // CHECK-FIXES: float floating = static_cast(boolean || anotherBoolean) * 0.3f; + float floating = (boolean || anotherBoolean) * 0.3f; + + // CHECK-MESSAGES: :[[@LINE+2]]:27: warning: implicit cast bool -> 'double' + // CHECK-FIXES: double doubleFloating = static_cast(boolean && (anotherBoolean || boolean)) * 0.3; + double doubleFloating = (boolean && (anotherBoolean || boolean)) * 0.3; +} + +void implicitCastFromBoolLiterals() { + // CHECK-MESSAGES: :[[@LINE+2]]:21: warning: implicit cast bool -> 'int' + // CHECK-FIXES: functionTakingInt(1); + functionTakingInt(true); + + // CHECK-MESSAGES: :[[@LINE+2]]:30: warning: implicit cast bool -> 'unsigned long' + // CHECK-FIXES: functionTakingUnsignedLong(0u); + functionTakingUnsignedLong(false); + + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast bool -> 'char' + // CHECK-FIXES: functionTakingChar(1); + functionTakingChar(true); + + // CHECK-MESSAGES: :[[@LINE+2]]:23: warning: implicit cast bool -> 'float' + // CHECK-FIXES: functionTakingFloat(0.0f); + functionTakingFloat(false); + + // CHECK-MESSAGES: :[[@LINE+2]]:24: warning: implicit cast bool -> 'double' + // CHECK-FIXES: functionTakingDouble(1.0); + functionTakingDouble(true); +} + +void implicitCastFromBoolInComparisons() { + bool boolean = true; + int integer = 0; + + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast bool -> 'int' + // CHECK-FIXES: functionTakingBool(static_cast(boolean) == integer); + functionTakingBool(boolean == integer); + + // CHECK-MESSAGES: :[[@LINE+2]]:33: warning: implicit cast bool -> 'int' + // CHECK-FIXES: functionTakingBool(integer != static_cast(boolean)); + functionTakingBool(integer != boolean); +} + +void ignoreBoolComparisons() { + bool boolean = true; + bool anotherBoolean = false; + + functionTakingBool(boolean == anotherBoolean); + functionTakingBool(boolean != anotherBoolean); +} + +void ignoreExplicitCastsFromBool() { + bool boolean = true; + + int integer = static_cast(boolean) + 3; + float floating = static_cast(boolean) * 0.3f; + char character = static_cast(boolean); +} + +void ignoreImplicitCastFromBoolInMacroExpansions() { + bool boolean = true; + + #define CAST_FROM_BOOL_IN_MACRO_BODY boolean + 3 + int integerFromMacroBody = CAST_FROM_BOOL_IN_MACRO_BODY; + + #define CAST_FROM_BOOL_IN_MACRO_ARGUMENT(x) x + 3 + int integerFromMacroArgument = CAST_FROM_BOOL_IN_MACRO_ARGUMENT(boolean); +} + +namespace ignoreImplicitCastFromBoolInTemplateInstantiations { + +template +void templateFunction() { + bool boolean = true; + T uknownType = boolean + 3; +} + +void useOfTemplateFunction() { + templateFunction(); +} + +} // namespace + +////////// Implicit cast to bool. + +void implicitCastToBoolSimpleCases() { + int integer = 10; + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'int' -> bool + // CHECK-FIXES: functionTakingBool(integer != 0); + functionTakingBool(integer); + + unsigned long unsignedLong = 10; + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'unsigned long' -> bool + // CHECK-FIXES: functionTakingBool(unsignedLong != 0u); + functionTakingBool(unsignedLong); + + float floating = 0.0f; + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'float' -> bool + // CHECK-FIXES: functionTakingBool(floating != 0.0f); + functionTakingBool(floating); + + double doubleFloating = 1.0f; + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'double' -> bool + // CHECK-FIXES: functionTakingBool(doubleFloating != 0.0); + functionTakingBool(doubleFloating); + + char character = 'a'; + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'char' -> bool + // CHECK-FIXES: functionTakingBool(character != 0); + functionTakingBool(character); + + int* pointer = nullptr; + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'int *' -> bool + // CHECK-FIXES: functionTakingBool(pointer != nullptr); + functionTakingBool(pointer); + + auto pointerToMember = &Struct::member; + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'int struct Struct::*' -> bool + // CHECK-FIXES: functionTakingBool(pointerToMember != nullptr); + functionTakingBool(pointerToMember); +} + +void implicitCastToBoolInSingleExpressions() { + int integer = 10; + // CHECK-MESSAGES: :[[@LINE+2]]:28: warning: implicit cast 'int' -> bool + // CHECK-FIXES: bool boolComingFromInt = integer != 0; + bool boolComingFromInt = integer; + + float floating = 10.0f; + // CHECK-MESSAGES: :[[@LINE+2]]:30: warning: implicit cast 'float' -> bool + // CHECK-FIXES: bool boolComingFromFloat = floating != 0.0f; + bool boolComingFromFloat = floating; + + char character = 'a'; + // CHECK-MESSAGES: :[[@LINE+2]]:29: warning: implicit cast 'char' -> bool + // CHECK-FIXES: bool boolComingFromChar = character != 0; + bool boolComingFromChar = character; + + int* pointer = nullptr; + // CHECK-MESSAGES: :[[@LINE+2]]:32: warning: implicit cast 'int *' -> bool + // CHECK-FIXES: bool boolComingFromPointer = pointer != nullptr; + bool boolComingFromPointer = pointer; +} + +void implicitCastToBoolInComplexExpressions() { + bool boolean = true; + + int integer = 10; + int anotherInteger = 20; + // CHECK-MESSAGES: :[[@LINE+2]]:32: warning: implicit cast 'int' -> bool + // CHECK-FIXES: bool boolComingFromInteger = (integer + anotherInteger) != 0; + bool boolComingFromInteger = integer + anotherInteger; + + float floating = 0.2f; + // CHECK-MESSAGES: :[[@LINE+2]]:33: warning: implicit cast 'float' -> bool + // CHECK-FIXES: bool boolComingFromFloating = ((floating - 0.3f) != 0.0f) || boolean; + bool boolComingFromFloating = floating - 0.3f || boolean; + + double doubleFloating = 0.3; + // CHECK-MESSAGES: :[[@LINE+2]]:39: warning: implicit cast 'double' -> bool + // CHECK-FIXES: bool boolComingFromDoubleFloating = ((doubleFloating - 0.4) != 0.0) && boolean; + bool boolComingFromDoubleFloating = (doubleFloating - 0.4) && boolean; +} + +void implicitCastInNegationExpressions() { + int integer = 10; + // CHECK-MESSAGES: :[[@LINE+2]]:36: warning: implicit cast 'int' -> bool + // CHECK-FIXES: bool boolComingFromNegatedInt = integer == 0; + bool boolComingFromNegatedInt = !integer; + + float floating = 10.0f; + // CHECK-MESSAGES: :[[@LINE+2]]:39: warning: implicit cast 'float' -> bool + // CHECK-FIXES: bool boolComingFromNegatedFloat = floating == 0.0f; + bool boolComingFromNegatedFloat = ! floating; + + char character = 'a'; + // CHECK-MESSAGES: :[[@LINE+2]]:39: warning: implicit cast 'char' -> bool + // CHECK-FIXES: bool boolComingFromNegatedChar = ( character == 0); + bool boolComingFromNegatedChar = (! character); + + int* pointer = nullptr; + // CHECK-MESSAGES: :[[@LINE+2]]:43: warning: implicit cast 'int *' -> bool + // CHECK-FIXES: bool boolComingFromNegatedPointer = pointer == nullptr; + bool boolComingFromNegatedPointer = not pointer; +} + +void implicitCastToBoolInControlStatements() { + int integer = 10; + // CHECK-MESSAGES: :[[@LINE+2]]:7: warning: implicit cast 'int' -> bool + // CHECK-FIXES: if (integer != 0) {} + if (integer) {} + + long int longInteger = 0.2f; + // CHECK-MESSAGES: :[[@LINE+2]]:9: warning: implicit cast 'long' -> bool + // CHECK-FIXES: for (;longInteger != 0;) {} + for (;longInteger;) {} + + float floating = 0.3f; + // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: implicit cast 'float' -> bool + // CHECK-FIXES: while (floating != 0.0f) {} + while (floating) {} + + double doubleFloating = 0.4; + // CHECK-MESSAGES: :[[@LINE+2]]:16: warning: implicit cast 'double' -> bool + // CHECK-FIXES: do {} while (doubleFloating != 0.0); + do {} while (doubleFloating); +} + +bool implicitCastToBoolInReturnValue() { + float floating = 1.0f; + // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: implicit cast 'float' -> bool + // CHECK-FIXES: return floating != 0.0f; + return floating; +} + +void implicitCastToBoolFromLiterals() { + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'int' -> bool + // CHECK-FIXES: functionTakingBool(false); + functionTakingBool(0); + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'int' -> bool + // CHECK-FIXES: functionTakingBool(true); + functionTakingBool(1); + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'unsigned long' -> bool + // CHECK-FIXES: functionTakingBool(true); + functionTakingBool(2ul); + + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'float' -> bool + // CHECK-FIXES: functionTakingBool(false); + functionTakingBool(0.0f); + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'float' -> bool + // CHECK-FIXES: functionTakingBool(true); + functionTakingBool(1.0f); + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'double' -> bool + // CHECK-FIXES: functionTakingBool(true); + functionTakingBool(2.0); + + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'char' -> bool + // CHECK-FIXES: functionTakingBool(false); + functionTakingBool('\0'); + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'char' -> bool + // CHECK-FIXES: functionTakingBool(true); + functionTakingBool('a'); + + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'const char *' -> bool + // CHECK-FIXES: functionTakingBool(true); + functionTakingBool(""); + + // CHECK-MESSAGES: :[[@LINE+2]]:22: warning: implicit cast 'const char *' -> bool + // CHECK-FIXES: functionTakingBool(true); + functionTakingBool("abc"); +} + +void ignoreExplicitCastsToBool() { + int integer = 10; + bool boolComingFromInt = static_cast(integer); + + float floating = 10.0f; + bool boolComingFromFloat = static_cast(floating); + + char character = 'a'; + bool boolComingFromChar = static_cast(character); + + int* pointer = nullptr; + bool booleanComingFromPointer = static_cast(pointer); +} + +void ignoreImplicitCastToBoolInMacroExpansions() { + int integer = 3; + + #define CAST_TO_BOOL_IN_MACRO_BODY integer && false + bool boolFromMacroBody = CAST_TO_BOOL_IN_MACRO_BODY; + + #define CAST_TO_BOOL_IN_MACRO_ARGUMENT(x) x || true + bool boolFromMacroArgument = CAST_TO_BOOL_IN_MACRO_ARGUMENT(integer); +} + +namespace ignoreImplicitCastToBoolInTemplateInstantiations { + +template +void templateFunction() { + T unknownType = 0; + bool boolean = unknownType; +} + +void useOfTemplateFunction() { + templateFunction(); +} + +} // namespace + +namespace ignoreUserDefinedConversionOperator { + +struct StructWithUserConversion { + operator bool(); +}; + +void useOfUserConversion() { + StructWithUserConversion structure; + functionTakingBool(structure); +} + +} // namespace