[clang-tidy] 'implicit cast' -> 'implicit conversion'

Summary:
This patch renames checks, check options and changes messages to use correct
term "implicit conversion" instead of "implicit cast" (which has been in use in
Clang AST since ~10 years, but it's still technically incorrect w.r.t. C++
standard).

  * performance-implicit-cast-in-loop -> performance-implicit-conversion-in-loop
  * readability-implicit-bool-cast -> readability-implicit-bool-conversion
    - readability-implicit-bool-cast.AllowConditionalIntegerCasts ->
      readability-implicit-bool-conversion.AllowIntegerConditions
    - readability-implicit-bool-cast.AllowConditionalPointerCasts ->
      readability-implicit-bool-conversion.AllowPointerConditions

Reviewers: hokein, jdennett

Reviewed By: hokein

Subscribers: mgorny, JDevlieghere, xazax.hun, cfe-commits

Differential Revision: https://reviews.llvm.org/D36456

llvm-svn: 310366
This commit is contained in:
Alexander Kornienko 2017-08-08 14:53:52 +00:00
parent 64d31edef3
commit f1a6552a95
18 changed files with 355 additions and 319 deletions

View File

@ -3,7 +3,7 @@ set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyPerformanceModule
FasterStringFindCheck.cpp
ForRangeCopyCheck.cpp
ImplicitCastInLoopCheck.cpp
ImplicitConversionInLoopCheck.cpp
InefficientStringConcatenationCheck.cpp
InefficientVectorOperationCheck.cpp
PerformanceTidyModule.cpp

View File

@ -1,4 +1,4 @@
//===--- ImplicitCastInLoopCheck.cpp - clang-tidy--------------------------===//
//===--- ImplicitConversionInLoopCheck.cpp - clang-tidy--------------------===//
//
// The LLVM Compiler Infrastructure
//
@ -7,7 +7,7 @@
//
//===----------------------------------------------------------------------===//
#include "ImplicitCastInLoopCheck.h"
#include "ImplicitConversionInLoopCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
@ -21,30 +21,30 @@ namespace clang {
namespace tidy {
namespace performance {
namespace {
// Checks if the stmt is a ImplicitCastExpr with a CastKind that is not a NoOp.
// The subtelty is that in some cases (user defined conversions), we can
// get to ImplicitCastExpr inside each other, with the outer one a NoOp. In this
// case we skip the first cast expr.
bool IsNonTrivialImplicitCast(const Stmt *ST) {
static bool IsNonTrivialImplicitCast(const Stmt *ST) {
if (const auto *ICE = dyn_cast<ImplicitCastExpr>(ST)) {
return (ICE->getCastKind() != CK_NoOp) ||
IsNonTrivialImplicitCast(ICE->getSubExpr());
}
return false;
}
} // namespace
void ImplicitCastInLoopCheck::registerMatchers(MatchFinder *Finder) {
void ImplicitConversionInLoopCheck::registerMatchers(MatchFinder *Finder) {
// We look for const ref loop variables that (optionally inside an
// ExprWithCleanup) materialize a temporary, and contain a implicit cast. The
// check on the implicit cast is done in check() because we can't access
// implicit cast subnode via matchers: has() skips casts and materialize!
// We also bind on the call to operator* to get the proper type in the
// diagnostic message.
// Note that when the implicit cast is done through a user defined cast
// operator, the node is a CXXMemberCallExpr, not a CXXOperatorCallExpr, so
// it should not get caught by the cxxOperatorCallExpr() matcher.
// ExprWithCleanup) materialize a temporary, and contain a implicit
// conversion. The check on the implicit conversion is done in check() because
// we can't access implicit conversion subnode via matchers: has() skips casts
// and materialize! We also bind on the call to operator* to get the proper
// type in the diagnostic message.
//
// Note that when the implicit conversion is done through a user defined
// conversion operator, the node is a CXXMemberCallExpr, not a
// CXXOperatorCallExpr, so it should not get caught by the
// cxxOperatorCallExpr() matcher.
Finder->addMatcher(
cxxForRangeStmt(hasLoopVariable(
varDecl(hasType(qualType(references(qualType(isConstQualified())))),
@ -55,7 +55,8 @@ void ImplicitCastInLoopCheck::registerMatchers(MatchFinder *Finder) {
this);
}
void ImplicitCastInLoopCheck::check(const MatchFinder::MatchResult &Result) {
void ImplicitConversionInLoopCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *VD = Result.Nodes.getNodeAs<VarDecl>("faulty-var");
const auto *Init = Result.Nodes.getNodeAs<Expr>("init");
const auto *OperatorCall =
@ -76,7 +77,7 @@ void ImplicitCastInLoopCheck::check(const MatchFinder::MatchResult &Result) {
ReportAndFix(Result.Context, VD, OperatorCall);
}
void ImplicitCastInLoopCheck::ReportAndFix(
void ImplicitConversionInLoopCheck::ReportAndFix(
const ASTContext *Context, const VarDecl *VD,
const CXXOperatorCallExpr *OperatorCall) {
// We only match on const ref, so we should print a const ref version of the
@ -85,8 +86,8 @@ void ImplicitCastInLoopCheck::ReportAndFix(
QualType ConstRefType = Context->getLValueReferenceType(ConstType);
const char Message[] =
"the type of the loop variable %0 is different from the one returned "
"by the iterator and generates an implicit cast; you can either "
"change the type to the correct one (%1 but 'const auto&' is always a "
"by the iterator and generates an implicit conversion; you can either "
"change the type to the matching one (%1 but 'const auto&' is always a "
"valid option) or remove the reference to make it explicit that you are "
"creating a new value";
diag(VD->getLocStart(), Message) << VD << ConstRefType;

View File

@ -1,4 +1,4 @@
//===--- ImplicitCastInLoopCheck.h - clang-tidy------------------*- C++ -*-===//
//===--- ImplicitConversionInLoopCheck.h - clang-tidy------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_IMPLICIT_CAST_IN_LOOP_CHECK_H_
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_IMPLICIT_CAST_IN_LOOP_CHECK_H_
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_IMPLICIT_CONVERSION_IN_LOOP_CHECK_H_
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_IMPLICIT_CONVERSION_IN_LOOP_CHECK_H_
#include "../ClangTidy.h"
@ -19,9 +19,9 @@ namespace performance {
// Checks that in a for range loop, if the provided type is a reference, then
// the underlying type is the one returned by the iterator (i.e. that there
// isn't any implicit conversion).
class ImplicitCastInLoopCheck : public ClangTidyCheck {
class ImplicitConversionInLoopCheck : public ClangTidyCheck {
public:
ImplicitCastInLoopCheck(StringRef Name, ClangTidyContext *Context)
ImplicitConversionInLoopCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
@ -35,4 +35,4 @@ private:
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_IMPLICIT_CAST_IN_LOOP_CHECK_H_
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_IMPLICIT_CONVERSION_IN_LOOP_CHECK_H_

View File

@ -12,7 +12,7 @@
#include "../ClangTidyModuleRegistry.h"
#include "FasterStringFindCheck.h"
#include "ForRangeCopyCheck.h"
#include "ImplicitCastInLoopCheck.h"
#include "ImplicitConversionInLoopCheck.h"
#include "InefficientStringConcatenationCheck.h"
#include "InefficientVectorOperationCheck.h"
#include "TypePromotionInMathFnCheck.h"
@ -30,8 +30,8 @@ public:
"performance-faster-string-find");
CheckFactories.registerCheck<ForRangeCopyCheck>(
"performance-for-range-copy");
CheckFactories.registerCheck<ImplicitCastInLoopCheck>(
"performance-implicit-cast-in-loop");
CheckFactories.registerCheck<ImplicitConversionInLoopCheck>(
"performance-implicit-conversion-in-loop");
CheckFactories.registerCheck<InefficientStringConcatenationCheck>(
"performance-inefficient-string-concatenation");
CheckFactories.registerCheck<InefficientVectorOperationCheck>(

View File

@ -9,7 +9,7 @@ add_clang_library(clangTidyReadabilityModule
ElseAfterReturnCheck.cpp
FunctionSizeCheck.cpp
IdentifierNamingCheck.cpp
ImplicitBoolCastCheck.cpp
ImplicitBoolConversionCheck.cpp
InconsistentDeclarationParameterNameCheck.cpp
MisleadingIndentationCheck.cpp
MisplacedArrayIndexCheck.cpp

View File

@ -1,4 +1,4 @@
//===--- ImplicitBoolCastCheck.cpp - clang-tidy----------------------------===//
//===--- ImplicitBoolConversionCheck.cpp - clang-tidy----------------------===//
//
// The LLVM Compiler Infrastructure
//
@ -7,7 +7,7 @@
//
//===----------------------------------------------------------------------===//
#include "ImplicitBoolCastCheck.h"
#include "ImplicitBoolConversionCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
@ -218,7 +218,7 @@ StringRef getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral,
return BoolLiteral->getValue() ? "1" : "0";
}
bool isAllowedConditionalCast(const ImplicitCastExpr *Cast,
bool isCastAllowedInCondition(const ImplicitCastExpr *Cast,
ASTContext &Context) {
std::queue<const Stmt *> Q;
Q.push(Cast);
@ -245,22 +245,19 @@ bool isAllowedConditionalCast(const ImplicitCastExpr *Cast,
} // anonymous namespace
ImplicitBoolCastCheck::ImplicitBoolCastCheck(StringRef Name,
ClangTidyContext *Context)
ImplicitBoolConversionCheck::ImplicitBoolConversionCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
AllowConditionalIntegerCasts(
Options.get("AllowConditionalIntegerCasts", false)),
AllowConditionalPointerCasts(
Options.get("AllowConditionalPointerCasts", false)) {}
AllowIntegerConditions(Options.get("AllowIntegerConditions", false)),
AllowPointerConditions(Options.get("AllowPointerConditions", false)) {}
void ImplicitBoolCastCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "AllowConditionalIntegerCasts",
AllowConditionalIntegerCasts);
Options.store(Opts, "AllowConditionalPointerCasts",
AllowConditionalPointerCasts);
void ImplicitBoolConversionCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "AllowIntegerConditions", AllowIntegerConditions);
Options.store(Opts, "AllowPointerConditions", AllowPointerConditions);
}
void ImplicitBoolCastCheck::registerMatchers(MatchFinder *Finder) {
void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
// This check doesn't make much sense if we run it on language without
// built-in bool support.
if (!getLangOpts().Bool) {
@ -326,7 +323,8 @@ void ImplicitBoolCastCheck::registerMatchers(MatchFinder *Finder) {
this);
}
void ImplicitBoolCastCheck::check(const MatchFinder::MatchResult &Result) {
void ImplicitBoolConversionCheck::check(
const MatchFinder::MatchResult &Result) {
if (const auto *CastToBool =
Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) {
const auto *Parent = Result.Nodes.getNodeAs<Stmt>("parentStmt");
@ -341,23 +339,22 @@ void ImplicitBoolCastCheck::check(const MatchFinder::MatchResult &Result) {
}
}
void ImplicitBoolCastCheck::handleCastToBool(const ImplicitCastExpr *Cast,
const Stmt *Parent,
ASTContext &Context) {
if (AllowConditionalPointerCasts &&
void ImplicitBoolConversionCheck::handleCastToBool(const ImplicitCastExpr *Cast,
const Stmt *Parent,
ASTContext &Context) {
if (AllowPointerConditions &&
(Cast->getCastKind() == CK_PointerToBoolean ||
Cast->getCastKind() == CK_MemberPointerToBoolean) &&
isAllowedConditionalCast(Cast, Context)) {
isCastAllowedInCondition(Cast, Context)) {
return;
}
if (AllowConditionalIntegerCasts &&
Cast->getCastKind() == CK_IntegralToBoolean &&
isAllowedConditionalCast(Cast, Context)) {
if (AllowIntegerConditions && Cast->getCastKind() == CK_IntegralToBoolean &&
isCastAllowedInCondition(Cast, Context)) {
return;
}
auto Diag = diag(Cast->getLocStart(), "implicit cast %0 -> bool")
auto Diag = diag(Cast->getLocStart(), "implicit conversion %0 -> bool")
<< Cast->getSubExpr()->getType();
StringRef EquivalentLiteral =
@ -369,12 +366,13 @@ void ImplicitBoolCastCheck::handleCastToBool(const ImplicitCastExpr *Cast,
}
}
void ImplicitBoolCastCheck::handleCastFromBool(
void ImplicitBoolConversionCheck::handleCastFromBool(
const ImplicitCastExpr *Cast, const ImplicitCastExpr *NextImplicitCast,
ASTContext &Context) {
QualType DestType =
NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
auto Diag = diag(Cast->getLocStart(), "implicit cast bool -> %0") << DestType;
auto Diag = diag(Cast->getLocStart(), "implicit conversion bool -> %0")
<< DestType;
if (const auto *BoolLiteral =
dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr())) {

View File

@ -1,4 +1,4 @@
//===--- ImplicitBoolCastCheck.h - clang-tidy--------------------*- C++ -*-===//
//===--- ImplicitBoolConversionCheck.h - clang-tidy--------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CAST_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CAST_H
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CONVERSION_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CONVERSION_H
#include "../ClangTidy.h"
@ -16,13 +16,13 @@ namespace clang {
namespace tidy {
namespace readability {
/// \brief Checks for use of implicit bool casts in expressions.
/// \brief Checks for use of implicit bool conversions 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 {
/// http://clang.llvm.org/extra/clang-tidy/checks/readability-implicit-bool-conversion.html
class ImplicitBoolConversionCheck : public ClangTidyCheck {
public:
ImplicitBoolCastCheck(StringRef Name, ClangTidyContext *Context);
ImplicitBoolConversionCheck(StringRef Name, ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
@ -35,12 +35,12 @@ private:
const ImplicitCastExpr *FurtherImplicitCastExpression,
ASTContext &Context);
bool AllowConditionalIntegerCasts;
bool AllowConditionalPointerCasts;
const bool AllowIntegerConditions;
const bool AllowPointerConditions;
};
} // namespace readability
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CAST_H
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CONVERSION_H

View File

@ -18,7 +18,7 @@
#include "ElseAfterReturnCheck.h"
#include "FunctionSizeCheck.h"
#include "IdentifierNamingCheck.h"
#include "ImplicitBoolCastCheck.h"
#include "ImplicitBoolConversionCheck.h"
#include "InconsistentDeclarationParameterNameCheck.h"
#include "MisleadingIndentationCheck.h"
#include "MisplacedArrayIndexCheck.h"
@ -58,8 +58,8 @@ public:
"readability-function-size");
CheckFactories.registerCheck<IdentifierNamingCheck>(
"readability-identifier-naming");
CheckFactories.registerCheck<ImplicitBoolCastCheck>(
"readability-implicit-bool-cast");
CheckFactories.registerCheck<ImplicitBoolConversionCheck>(
"readability-implicit-bool-conversion");
CheckFactories.registerCheck<InconsistentDeclarationParameterNameCheck>(
"readability-inconsistent-declaration-parameter-name");
CheckFactories.registerCheck<MisleadingIndentationCheck>(

View File

@ -57,7 +57,19 @@ The improvements are...
Improvements to clang-tidy
--------------------------
The improvements are...
* Renamed checks to use correct term "implicit conversion" instead of "implicit
cast" and modified messages and option names accordingly:
- **performance-implicit-cast-in-loop** was renamed to
`performance-implicit-conversion-in-loop
<http://clang.llvm.org/extra/clang-tidy/checks/performance-implicit-conversion-in-loop.html>`_
- **readability-implicit-bool-cast** was renamed to
`readability-implicit-bool-conversion
<http://clang.llvm.org/extra/clang-tidy/checks/readability-implicit-bool-conversion.html>`_;
the check's options were renamed as follows:
``AllowConditionalIntegerCasts`` -> ``AllowIntegerConditions``,
``AllowConditionalPointerCasts`` -> ``AllowPointerConditions``.
Improvements to include-fixer
-----------------------------

View File

@ -150,7 +150,7 @@ Clang-Tidy Checks
mpi-type-mismatch
performance-faster-string-find
performance-for-range-copy
performance-implicit-cast-in-loop
performance-implicit-conversion-in-loop
performance-inefficient-string-concatenation
performance-inefficient-vector-operation
performance-type-promotion-in-math-fn
@ -164,7 +164,7 @@ Clang-Tidy Checks
readability-else-after-return
readability-function-size
readability-identifier-naming
readability-implicit-bool-cast
readability-implicit-bool-conversion
readability-inconsistent-declaration-parameter-name
readability-misleading-indentation
readability-misplaced-array-index

View File

@ -1,21 +1,12 @@
:orphan:
.. title:: clang-tidy - performance-implicit-cast-in-loop
.. meta::
:http-equiv=refresh: 5;URL=performance-implicit-conversion-in-loop.html
performance-implicit-cast-in-loop
=================================
This warning appears in a range-based loop with a loop variable of const ref
type where the type of the variable does not match the one returned by the
iterator. This means that an implicit cast has been added, which can for example
result in expensive deep copies.
This check has been renamed to `performance-implicit-conversion-in-loop
<performance-implicit-conversion-in-loop.html>`_.
Example:
.. code-block:: c++
map<int, vector<string>> my_map;
for (const pair<int, vector<string>>& p : my_map) {}
// The iterator type is in fact pair<const int, vector<string>>, which means
// that the compiler added a cast, resulting in a copy of the vectors.
The easiest solution is usually to use ``const auto&`` instead of writing the type
manually.

View File

@ -0,0 +1,21 @@
.. title:: clang-tidy - performance-implicit-conversion-in-loop
performance-implicit-conversion-in-loop
=======================================
This warning appears in a range-based loop with a loop variable of const ref
type where the type of the variable does not match the one returned by the
iterator. This means that an implicit conversion happens, which can for example
result in expensive deep copies.
Example:
.. code-block:: c++
map<int, vector<string>> my_map;
for (const pair<int, vector<string>>& p : my_map) {}
// The iterator type is in fact pair<const int, vector<string>>, which means
// that the compiler added a conversion, resulting in a copy of the vectors.
The easiest solution is usually to use ``const auto&`` instead of writing the
type manually.

View File

@ -1,130 +1,11 @@
:orphan:
.. title:: clang-tidy - readability-implicit-bool-cast
.. meta::
:http-equiv=refresh: 5;URL=readability-implicit-bool-conversion.html
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-block:: 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
occurrences of ``bool``, and the remaining code is no longer correct, yet it
still compiles without any visible warnings.
In addition to issuing warnings, fix-it hints are provided to help solve the
reported issues. This can be used for improving readability of code, for
example:
.. code-block:: 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 functionTakingInt(int param);
void conversionsFromBool() {
bool boolean;
functionTakingInt(boolean);
// ^ propose replacement: functionTakingInt(static_cast<int>(boolean));
functionTakingInt(true);
// ^ propose replacement: functionTakingInt(1);
}
In general, the following cast types are checked:
- integer expression/literal to boolean,
- floating expression/literal to boolean,
- pointer/pointer to member/``nullptr``/``NULL`` to boolean,
- boolean expression/literal to integer,
- boolean expression/literal to floating.
The rules for generating fix-it 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<int>(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``).
Some additional accommodations are made for pre-C++11 dialects:
- ``false`` literal cast to pointer is detected,
- instead of ``nullptr`` literal, ``0`` is proposed as replacement.
Occurrences of implicit casts inside macros and template instantiations are
deliberately ignored, as it is not clear how to deal with such cases.
Options
-------
.. option:: AllowConditionalIntegerCasts
When non-zero, the check will allow conditional integer casts. Default is
`0`.
.. option:: AllowConditionalPointerCasts
When non-zero, the check will allow conditional pointer casts. Default is `0`.
This check has been renamed to `readability-implicit-bool-conversion
<readability-implicit-bool-conversion.html>`_.

View File

@ -0,0 +1,132 @@
.. title:: clang-tidy - readability-implicit-bool-conversion
readability-implicit-bool-conversion
====================================
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`` conversion:
.. code-block:: c++
class Foo {
int m_foo;
public:
void setFoo(bool foo) { m_foo = foo; } // warning: implicit conversion bool -> int
int getFoo() { return m_foo; }
};
void use(Foo& foo) {
bool value = foo.getFoo(); // warning: implicit conversion 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
occurrences of ``bool``, and the remaining code is no longer correct, yet it
still compiles without any visible warnings.
In addition to issuing warnings, fix-it hints are provided to help solve the
reported issues. This can be used for improving readability of code, for
example:
.. code-block:: 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 functionTakingInt(int param);
void conversionsFromBool() {
bool boolean;
functionTakingInt(boolean);
// ^ propose replacement: functionTakingInt(static_cast<int>(boolean));
functionTakingInt(true);
// ^ propose replacement: functionTakingInt(1);
}
In general, the following conversion types are checked:
- integer expression/literal to boolean,
- floating expression/literal to boolean,
- pointer/pointer to member/``nullptr``/``NULL`` to boolean,
- boolean expression/literal to integer,
- boolean expression/literal to floating.
The rules for generating fix-it hints are:
- in case of conversions 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 conversion to bool, the proposed replacement
with comparison is simplified:
- ``if (!pointer)`` is changed to ``if (pointer == nullptr)``,
- in case of conversions from bool to other built-in types, an explicit
``static_cast`` is proposed to make it clear that a conversion is taking
place:
- ``int integer = boolean;`` is changed to
``int integer = static_cast<int>(boolean);``,
- if the conversion 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``).
Some additional accommodations are made for pre-C++11 dialects:
- ``false`` literal conversion to pointer is detected,
- instead of ``nullptr`` literal, ``0`` is proposed as replacement.
Occurrences of implicit conversions inside macros and template instantiations
are deliberately ignored, as it is not clear how to deal with such cases.
Options
-------
.. option:: AllowIntegerConditions
When non-zero, the check will allow conditional integer conversions. Default
is `0`.
.. option:: AllowPointerConditions
When non-zero, the check will allow conditional pointer conversions. Default
is `0`.

View File

@ -1,4 +1,4 @@
// RUN: %check_clang_tidy %s performance-implicit-cast-in-loop %t
// RUN: %check_clang_tidy %s performance-implicit-conversion-in-loop %t
// ---------- Classes used in the tests ----------
@ -26,8 +26,8 @@ struct View {
T end();
};
// With this class, the implicit cast is a call to the (implicit) constructor of
// the class.
// With this class, the implicit conversion is a call to the (implicit)
// constructor of the class.
template <typename T>
class ImplicitWrapper {
public:
@ -35,8 +35,8 @@ class ImplicitWrapper {
ImplicitWrapper(const T& t);
};
// With this class, the implicit cast is a call to the conversion operators of
// SimpleClass and ComplexClass.
// With this class, the implicit conversion is a call to the conversion
// operators of SimpleClass and ComplexClass.
template <typename T>
class OperatorWrapper {
public:
@ -98,7 +98,7 @@ void ComplexClassRefIterator() {
void ImplicitSimpleClassIterator() {
for (const ImplicitWrapper<SimpleClass>& foo : SimpleView()) {}
// CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the loop variable 'foo' is different from the one returned by the iterator and generates an implicit cast; you can either change the type to the correct one ('const SimpleClass &' but 'const auto&' is always a valid option) or remove the reference to make it explicit that you are creating a new value [performance-implicit-cast-in-loop]
// CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the loop variable 'foo' is different from the one returned by the iterator and generates an implicit conversion; you can either change the type to the matching one ('const SimpleClass &' but 'const auto&' is always a valid option) or remove the reference to make it explicit that you are creating a new value [performance-implicit-conversion-in-loop]
// for (ImplicitWrapper<SimpleClass>& foo : SimpleView()) {}
for (const ImplicitWrapper<SimpleClass> foo : SimpleView()) {}
for (ImplicitWrapper<SimpleClass>foo : SimpleView()) {}

View File

@ -1,7 +1,7 @@
// RUN: %check_clang_tidy %s readability-implicit-bool-cast %t \
// RUN: %check_clang_tidy %s readability-implicit-bool-conversion %t \
// RUN: -config='{CheckOptions: \
// RUN: [{key: readability-implicit-bool-cast.AllowConditionalIntegerCasts, value: 1}, \
// RUN: {key: readability-implicit-bool-cast.AllowConditionalPointerCasts, value: 1}]}' \
// RUN: [{key: readability-implicit-bool-conversion.AllowIntegerConditions, value: 1}, \
// RUN: {key: readability-implicit-bool-conversion.AllowPointerConditions, value: 1}]}' \
// RUN: -- -std=c++11
template<typename T>
@ -15,14 +15,14 @@ struct Struct {
};
void regularImplicitCastIntegerToBoolIsNotIgnored() {
void regularImplicitConversionIntegerToBoolIsNotIgnored() {
int integer = 0;
functionTaking<bool>(integer);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool [readability-implicit-bool-cast]
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool [readability-implicit-bool-conversion]
// CHECK-FIXES: functionTaking<bool>(integer != 0);
}
void implicitCastIntegerToBoolInConditionalsIsAllowed() {
void implicitConversionIntegerToBoolInConditionalsIsAllowed() {
if (functionReturningInt()) {}
if (!functionReturningInt()) {}
if (functionReturningInt() && functionReturningPointer()) {}
@ -40,19 +40,19 @@ void implicitCastIntegerToBoolInConditionalsIsAllowed() {
int *p1 = functionReturningPointer() ?: &value3;
}
void regularImplicitCastPointerToBoolIsNotIgnored() {
void regularImplicitConversionPointerToBoolIsNotIgnored() {
int* pointer = nullptr;
functionTaking<bool>(pointer);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int *' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int *' -> bool
// CHECK-FIXES: functionTaking<bool>(pointer != nullptr);
int Struct::* memberPointer = &Struct::member;
functionTaking<bool>(memberPointer);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int Struct::*' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int Struct::*' -> bool
// CHECK-FIXES: functionTaking<bool>(memberPointer != nullptr);
}
void implicitCastPointerToBoolInConditionalsIsAllowed() {
void implicitConversionPointerToBoolInConditionalsIsAllowed() {
if (functionReturningPointer()) {}
if (not functionReturningPointer()) {}
int value1 = functionReturningPointer() ? 1 : 2;

View File

@ -1,4 +1,4 @@
// RUN: %check_clang_tidy %s readability-implicit-bool-cast %t -- -- -std=c++98
// RUN: %check_clang_tidy %s readability-implicit-bool-conversion %t -- -- -std=c++98
// We need NULL macro, but some buildbots don't like including <cstddef> header
// This is a portable way of getting it to work
@ -15,31 +15,31 @@ struct Struct {
void useOldNullMacroInReplacements() {
int* pointer = NULL;
functionTaking<bool>(pointer);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int *' -> bool [readability-implicit-bool-cast]
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int *' -> bool [readability-implicit-bool-conversion]
// CHECK-FIXES: functionTaking<bool>(pointer != 0);
int Struct::* memberPointer = NULL;
functionTaking<bool>(!memberPointer);
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: implicit cast 'int Struct::*' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: implicit conversion 'int Struct::*' -> bool
// CHECK-FIXES: functionTaking<bool>(memberPointer == 0);
}
void fixFalseLiteralConvertingToNullPointer() {
functionTaking<int*>(false);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast bool -> 'int *'
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion bool -> 'int *'
// CHECK-FIXES: functionTaking<int*>(0);
int* pointer = NULL;
if (pointer == false) {}
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: implicit cast bool -> 'int *'
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: implicit conversion bool -> 'int *'
// CHECK-FIXES: if (pointer == 0) {}
functionTaking<int Struct::*>(false);
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit cast bool -> 'int Struct::*'
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit conversion bool -> 'int Struct::*'
// CHECK-FIXES: functionTaking<int Struct::*>(0);
int Struct::* memberPointer = NULL;
if (memberPointer != false) {}
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast bool -> 'int Struct::*'
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion bool -> 'int Struct::*'
// CHECK-FIXES: if (memberPointer != 0) {}
}

View File

@ -1,4 +1,4 @@
// RUN: %check_clang_tidy %s readability-implicit-bool-cast %t
// RUN: %check_clang_tidy %s readability-implicit-bool-conversion %t
// We need NULL macro, but some buildbots don't like including <cstddef> header
// This is a portable way of getting it to work
@ -13,42 +13,42 @@ struct Struct {
};
////////// Implicit cast from bool.
////////// Implicit conversion from bool.
void implicitCastFromBoolSimpleCases() {
void implicitConversionFromBoolSimpleCases() {
bool boolean = true;
functionTaking<bool>(boolean);
functionTaking<int>(boolean);
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: implicit cast bool -> 'int' [readability-implicit-bool-cast]
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: implicit conversion bool -> 'int' [readability-implicit-bool-conversion]
// CHECK-FIXES: functionTaking<int>(static_cast<int>(boolean));
functionTaking<unsigned long>(boolean);
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit cast bool -> 'unsigned long'
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit conversion bool -> 'unsigned long'
// CHECK-FIXES: functionTaking<unsigned long>(static_cast<unsigned long>(boolean));
functionTaking<char>(boolean);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast bool -> 'char'
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion bool -> 'char'
// CHECK-FIXES: functionTaking<char>(static_cast<char>(boolean));
functionTaking<float>(boolean);
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: implicit cast bool -> 'float'
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: implicit conversion bool -> 'float'
// CHECK-FIXES: functionTaking<float>(static_cast<float>(boolean));
functionTaking<double>(boolean);
// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: implicit cast bool -> 'double'
// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: implicit conversion bool -> 'double'
// CHECK-FIXES: functionTaking<double>(static_cast<double>(boolean));
}
float implicitCastFromBoolInReturnValue() {
float implicitConversionFromBoolInReturnValue() {
bool boolean = false;
return boolean;
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit cast bool -> 'float'
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit conversion bool -> 'float'
// CHECK-FIXES: return static_cast<float>(boolean);
}
void implicitCastFromBoolInSingleBoolExpressions(bool b1, bool b2) {
void implicitConversionFromBoolInSingleBoolExpressions(bool b1, bool b2) {
bool boolean = true;
boolean = b1 ^ b2;
boolean = b1 && b2;
@ -58,71 +58,71 @@ void implicitCastFromBoolInSingleBoolExpressions(bool b1, bool b2) {
boolean = b2 != false;
int integer = boolean - 3;
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: implicit cast bool -> 'int'
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: implicit conversion bool -> 'int'
// CHECK-FIXES: int integer = static_cast<int>(boolean) - 3;
float floating = boolean / 0.3f;
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit cast bool -> 'float'
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit conversion bool -> 'float'
// CHECK-FIXES: float floating = static_cast<float>(boolean) / 0.3f;
char character = boolean;
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit cast bool -> 'char'
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit conversion bool -> 'char'
// CHECK-FIXES: char character = static_cast<char>(boolean);
}
void implicitCastFromBoollInComplexBoolExpressions() {
void implicitConversionFromBoollInComplexBoolExpressions() {
bool boolean = true;
bool anotherBoolean = false;
int integer = boolean && anotherBoolean;
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: implicit cast bool -> 'int'
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: implicit conversion bool -> 'int'
// CHECK-FIXES: int integer = static_cast<int>(boolean && anotherBoolean);
unsigned long unsignedLong = (! boolean) + 4ul;
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: implicit cast bool -> 'unsigned long'
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: implicit conversion bool -> 'unsigned long'
// CHECK-FIXES: unsigned long unsignedLong = static_cast<unsigned long>(! boolean) + 4ul;
float floating = (boolean || anotherBoolean) * 0.3f;
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit cast bool -> 'float'
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit conversion bool -> 'float'
// CHECK-FIXES: float floating = static_cast<float>(boolean || anotherBoolean) * 0.3f;
double doubleFloating = (boolean && (anotherBoolean || boolean)) * 0.3;
// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: implicit cast bool -> 'double'
// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: implicit conversion bool -> 'double'
// CHECK-FIXES: double doubleFloating = static_cast<double>(boolean && (anotherBoolean || boolean)) * 0.3;
}
void implicitCastFromBoolLiterals() {
void implicitConversionFromBoolLiterals() {
functionTaking<int>(true);
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: implicit cast bool -> 'int'
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: implicit conversion bool -> 'int'
// CHECK-FIXES: functionTaking<int>(1);
functionTaking<unsigned long>(false);
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit cast bool -> 'unsigned long'
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit conversion bool -> 'unsigned long'
// CHECK-FIXES: functionTaking<unsigned long>(0u);
functionTaking<signed char>(true);
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: implicit cast bool -> 'signed char'
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: implicit conversion bool -> 'signed char'
// CHECK-FIXES: functionTaking<signed char>(1);
functionTaking<float>(false);
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: implicit cast bool -> 'float'
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: implicit conversion bool -> 'float'
// CHECK-FIXES: functionTaking<float>(0.0f);
functionTaking<double>(true);
// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: implicit cast bool -> 'double'
// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: implicit conversion bool -> 'double'
// CHECK-FIXES: functionTaking<double>(1.0);
}
void implicitCastFromBoolInComparisons() {
void implicitConversionFromBoolInComparisons() {
bool boolean = true;
int integer = 0;
functionTaking<bool>(boolean == integer);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast bool -> 'int'
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion bool -> 'int'
// CHECK-FIXES: functionTaking<bool>(static_cast<int>(boolean) == integer);
functionTaking<bool>(integer != boolean);
// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: implicit cast bool -> 'int'
// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: implicit conversion bool -> 'int'
// CHECK-FIXES: functionTaking<bool>(integer != static_cast<int>(boolean));
}
@ -142,7 +142,7 @@ void ignoreExplicitCastsFromBool() {
char character = static_cast<char>(boolean);
}
void ignoreImplicitCastFromBoolInMacroExpansions() {
void ignoreImplicitConversionFromBoolInMacroExpansions() {
bool boolean = true;
#define CAST_FROM_BOOL_IN_MACRO_BODY boolean + 3
@ -152,7 +152,7 @@ void ignoreImplicitCastFromBoolInMacroExpansions() {
int integerFromMacroArgument = CAST_FROM_BOOL_IN_MACRO_ARGUMENT(boolean);
}
namespace ignoreImplicitCastFromBoolInTemplateInstantiations {
namespace ignoreImplicitConversionFromBoolInTemplateInstantiations {
template<typename T>
void templateFunction() {
@ -164,204 +164,204 @@ void useOfTemplateFunction() {
templateFunction<int>();
}
} // namespace ignoreImplicitCastFromBoolInTemplateInstantiations
} // namespace ignoreImplicitConversionFromBoolInTemplateInstantiations
////////// Implicit cast to bool.
////////// Implicit conversions to bool.
void implicitCastToBoolSimpleCases() {
void implicitConversionToBoolSimpleCases() {
int integer = 10;
functionTaking<bool>(integer);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool
// CHECK-FIXES: functionTaking<bool>(integer != 0);
unsigned long unsignedLong = 10;
functionTaking<bool>(unsignedLong);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'unsigned long' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'unsigned long' -> bool
// CHECK-FIXES: functionTaking<bool>(unsignedLong != 0u);
float floating = 0.0f;
functionTaking<bool>(floating);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'float' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'float' -> bool
// CHECK-FIXES: functionTaking<bool>(floating != 0.0f);
double doubleFloating = 1.0f;
functionTaking<bool>(doubleFloating);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'double' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'double' -> bool
// CHECK-FIXES: functionTaking<bool>(doubleFloating != 0.0);
signed char character = 'a';
functionTaking<bool>(character);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'signed char' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'signed char' -> bool
// CHECK-FIXES: functionTaking<bool>(character != 0);
int* pointer = nullptr;
functionTaking<bool>(pointer);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int *' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int *' -> bool
// CHECK-FIXES: functionTaking<bool>(pointer != nullptr);
auto pointerToMember = &Struct::member;
functionTaking<bool>(pointerToMember);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int Struct::*' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int Struct::*' -> bool
// CHECK-FIXES: functionTaking<bool>(pointerToMember != nullptr);
}
void implicitCastToBoolInSingleExpressions() {
void implicitConversionToBoolInSingleExpressions() {
int integer = 10;
bool boolComingFromInt = integer;
// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: implicit cast 'int' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: implicit conversion 'int' -> bool
// CHECK-FIXES: bool boolComingFromInt = integer != 0;
float floating = 10.0f;
bool boolComingFromFloat = floating;
// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: implicit cast 'float' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: implicit conversion 'float' -> bool
// CHECK-FIXES: bool boolComingFromFloat = floating != 0.0f;
signed char character = 'a';
bool boolComingFromChar = character;
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: implicit cast 'signed char' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: implicit conversion 'signed char' -> bool
// CHECK-FIXES: bool boolComingFromChar = character != 0;
int* pointer = nullptr;
bool boolComingFromPointer = pointer;
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: implicit cast 'int *' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: implicit conversion 'int *' -> bool
// CHECK-FIXES: bool boolComingFromPointer = pointer != nullptr;
}
void implicitCastToBoolInComplexExpressions() {
void implicitConversionToBoolInComplexExpressions() {
bool boolean = true;
int integer = 10;
int anotherInteger = 20;
bool boolComingFromInteger = integer + anotherInteger;
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: implicit cast 'int' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: implicit conversion 'int' -> bool
// CHECK-FIXES: bool boolComingFromInteger = (integer + anotherInteger) != 0;
float floating = 0.2f;
bool boolComingFromFloating = floating - 0.3f || boolean;
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit cast 'float' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit conversion 'float' -> bool
// CHECK-FIXES: bool boolComingFromFloating = ((floating - 0.3f) != 0.0f) || boolean;
double doubleFloating = 0.3;
bool boolComingFromDoubleFloating = (doubleFloating - 0.4) && boolean;
// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: implicit cast 'double' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: implicit conversion 'double' -> bool
// CHECK-FIXES: bool boolComingFromDoubleFloating = ((doubleFloating - 0.4) != 0.0) && boolean;
}
void implicitCastInNegationExpressions() {
void implicitConversionInNegationExpressions() {
int integer = 10;
bool boolComingFromNegatedInt = !integer;
// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: implicit cast 'int' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: implicit conversion 'int' -> bool
// CHECK-FIXES: bool boolComingFromNegatedInt = integer == 0;
float floating = 10.0f;
bool boolComingFromNegatedFloat = ! floating;
// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: implicit cast 'float' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: implicit conversion 'float' -> bool
// CHECK-FIXES: bool boolComingFromNegatedFloat = floating == 0.0f;
signed char character = 'a';
bool boolComingFromNegatedChar = (! character);
// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: implicit cast 'signed char' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: implicit conversion 'signed char' -> bool
// CHECK-FIXES: bool boolComingFromNegatedChar = (character == 0);
int* pointer = nullptr;
bool boolComingFromNegatedPointer = not pointer;
// CHECK-MESSAGES: :[[@LINE-1]]:43: warning: implicit cast 'int *' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:43: warning: implicit conversion 'int *' -> bool
// CHECK-FIXES: bool boolComingFromNegatedPointer = pointer == nullptr;
}
void implicitCastToBoolInControlStatements() {
void implicitConversionToBoolInControlStatements() {
int integer = 10;
if (integer) {}
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: implicit cast 'int' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: implicit conversion 'int' -> bool
// CHECK-FIXES: if (integer != 0) {}
long int longInteger = 0.2f;
for (;longInteger;) {}
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: implicit cast 'long' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: implicit conversion 'long' -> bool
// CHECK-FIXES: for (;longInteger != 0;) {}
float floating = 0.3f;
while (floating) {}
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit cast 'float' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit conversion 'float' -> bool
// CHECK-FIXES: while (floating != 0.0f) {}
double doubleFloating = 0.4;
do {} while (doubleFloating);
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: implicit cast 'double' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: implicit conversion 'double' -> bool
// CHECK-FIXES: do {} while (doubleFloating != 0.0);
}
bool implicitCastToBoolInReturnValue() {
bool implicitConversionToBoolInReturnValue() {
float floating = 1.0f;
return floating;
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit cast 'float' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit conversion 'float' -> bool
// CHECK-FIXES: return floating != 0.0f;
}
void implicitCastToBoolFromLiterals() {
void implicitConversionToBoolFromLiterals() {
functionTaking<bool>(0);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool
// CHECK-FIXES: functionTaking<bool>(false);
functionTaking<bool>(1);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool
// CHECK-FIXES: functionTaking<bool>(true);
functionTaking<bool>(2ul);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'unsigned long' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'unsigned long' -> bool
// CHECK-FIXES: functionTaking<bool>(true);
functionTaking<bool>(0.0f);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'float' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'float' -> bool
// CHECK-FIXES: functionTaking<bool>(false);
functionTaking<bool>(1.0f);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'float' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'float' -> bool
// CHECK-FIXES: functionTaking<bool>(true);
functionTaking<bool>(2.0);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'double' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'double' -> bool
// CHECK-FIXES: functionTaking<bool>(true);
functionTaking<bool>('\0');
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'char' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'char' -> bool
// CHECK-FIXES: functionTaking<bool>(false);
functionTaking<bool>('a');
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'char' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'char' -> bool
// CHECK-FIXES: functionTaking<bool>(true);
functionTaking<bool>("");
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'const char *' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'const char *' -> bool
// CHECK-FIXES: functionTaking<bool>(true);
functionTaking<bool>("abc");
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'const char *' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'const char *' -> bool
// CHECK-FIXES: functionTaking<bool>(true);
functionTaking<bool>(NULL);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'long' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'long' -> bool
// CHECK-FIXES: functionTaking<bool>(false);
}
void implicitCastToBoolFromUnaryMinusAndZeroLiterals() {
void implicitConversionToBoolFromUnaryMinusAndZeroLiterals() {
functionTaking<bool>(-0);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool
// CHECK-FIXES: functionTaking<bool>((-0) != 0);
functionTaking<bool>(-0.0f);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'float' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'float' -> bool
// CHECK-FIXES: functionTaking<bool>((-0.0f) != 0.0f);
functionTaking<bool>(-0.0);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'double' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'double' -> bool
// CHECK-FIXES: functionTaking<bool>((-0.0) != 0.0);
}
void implicitCastToBoolInWithOverloadedOperators() {
void implicitConversionToBoolInWithOverloadedOperators() {
struct UserStruct {
int operator()(int x) { return x; }
int operator+(int y) { return y; }
@ -370,18 +370,18 @@ void implicitCastToBoolInWithOverloadedOperators() {
UserStruct s;
functionTaking<bool>(s(0));
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool
// CHECK-FIXES: functionTaking<bool>(s(0) != 0);
functionTaking<bool>(s + 2);
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool
// CHECK-FIXES: functionTaking<bool>((s + 2) != 0);
}
int functionReturningInt();
int* functionReturningPointer();
void ignoreImplicitCastToBoolWhenDeclaringVariableInControlStatements() {
void ignoreImplicitConversionToBoolWhenDeclaringVariableInControlStatements() {
if (int integer = functionReturningInt()) {}
while (int* pointer = functionReturningPointer()) {}
@ -401,7 +401,7 @@ void ignoreExplicitCastsToBool() {
bool booleanComingFromPointer = static_cast<bool>(pointer);
}
void ignoreImplicitCastToBoolInMacroExpansions() {
void ignoreImplicitConversionToBoolInMacroExpansions() {
int integer = 3;
#define CAST_TO_BOOL_IN_MACRO_BODY integer && false
@ -411,7 +411,7 @@ void ignoreImplicitCastToBoolInMacroExpansions() {
bool boolFromMacroArgument = CAST_TO_BOOL_IN_MACRO_ARGUMENT(integer);
}
namespace ignoreImplicitCastToBoolInTemplateInstantiations {
namespace ignoreImplicitConversionToBoolInTemplateInstantiations {
template<typename T>
void templateFunction() {
@ -423,7 +423,7 @@ void useOfTemplateFunction() {
templateFunction<int>();
}
} // namespace ignoreImplicitCastToBoolInTemplateInstantiations
} // namespace ignoreImplicitConversionToBoolInTemplateInstantiations
namespace ignoreUserDefinedConversionOperator {