Files
acceptance-tests
data
docs
external
Newtonsoft.Json
api-doc-tools
api-snapshot
aspnetwebstack
bdwgc
binary-reference-assemblies
bockbuild
boringssl
cecil
cecil-legacy
corefx
corert
helix-binaries
ikdasm
ikvm
illinker-test-assets
linker
llvm-project
clang
clang-tools-extra
change-namespace
clang-apply-replacements
clang-move
clang-query
clang-reorder-fields
clang-tidy
android
boost
bugprone
cert
cppcoreguidelines
fuchsia
google
hicpp
llvm
misc
modernize
mpi
objc
performance
plugin
readability
AvoidConstParamsInDecls.cpp
AvoidConstParamsInDecls.h
BracesAroundStatementsCheck.cpp
BracesAroundStatementsCheck.h
CMakeLists.txt
ContainerSizeEmptyCheck.cpp
ContainerSizeEmptyCheck.h
DeleteNullPointerCheck.cpp
DeleteNullPointerCheck.h
DeletedDefaultCheck.cpp
DeletedDefaultCheck.h
ElseAfterReturnCheck.cpp
ElseAfterReturnCheck.h
FunctionSizeCheck.cpp
FunctionSizeCheck.h
IdentifierNamingCheck.cpp
IdentifierNamingCheck.h
ImplicitBoolConversionCheck.cpp
ImplicitBoolConversionCheck.h
InconsistentDeclarationParameterNameCheck.cpp
InconsistentDeclarationParameterNameCheck.h
MisleadingIndentationCheck.cpp
MisleadingIndentationCheck.h
MisplacedArrayIndexCheck.cpp
MisplacedArrayIndexCheck.h
NamedParameterCheck.cpp
NamedParameterCheck.h
NamespaceCommentCheck.cpp
NamespaceCommentCheck.h
NonConstParameterCheck.cpp
NonConstParameterCheck.h
ReadabilityTidyModule.cpp
RedundantControlFlowCheck.cpp
RedundantControlFlowCheck.h
RedundantDeclarationCheck.cpp
RedundantDeclarationCheck.h
RedundantFunctionPtrDereferenceCheck.cpp
RedundantFunctionPtrDereferenceCheck.h
RedundantMemberInitCheck.cpp
RedundantMemberInitCheck.h
RedundantSmartptrGetCheck.cpp
RedundantSmartptrGetCheck.h
RedundantStringCStrCheck.cpp
RedundantStringCStrCheck.h
RedundantStringInitCheck.cpp
RedundantStringInitCheck.h
SimplifyBooleanExprCheck.cpp
SimplifyBooleanExprCheck.h
StaticAccessedThroughInstanceCheck.cpp
StaticAccessedThroughInstanceCheck.h
StaticDefinitionInAnonymousNamespaceCheck.cpp
StaticDefinitionInAnonymousNamespaceCheck.h
UniqueptrDeleteReleaseCheck.cpp
UniqueptrDeleteReleaseCheck.h
tool
utils
CMakeLists.txt
ClangTidy.cpp
ClangTidy.h
ClangTidyDiagnosticConsumer.cpp
ClangTidyDiagnosticConsumer.h
ClangTidyModule.cpp
ClangTidyModule.h
ClangTidyModuleRegistry.h
ClangTidyOptions.cpp
ClangTidyOptions.h
add_new_check.py
rename_check.py
clang-tidy-vs
clangd
docs
include-fixer
modularize
pp-trace
test
tool-template
unittests
.arcconfig
.gitignore
CMakeLists.txt
CODE_OWNERS.TXT
LICENSE.TXT
README.txt
compiler-rt
eng
libcxx
libcxxabi
libunwind
lld
lldb
llvm
nuget
openmp
polly
Directory.Build.props
Directory.Build.targets
NuGet.config
azure-pipelines.yml
build.cmd
build.sh
dir.common.props
global.json
llvm.proj
mxe-Win64.cmake.in
nuget-buildtasks
nunit-lite
roslyn-binaries
rx
xunit-binaries
how-to-bump-roslyn-binaries.md
ikvm-native
llvm
m4
man
mcs
mono
msvc
netcore
po
runtime
samples
scripts
support
tools
COPYING.LIB
LICENSE
Makefile.am
Makefile.in
NEWS
README.md
acinclude.m4
aclocal.m4
autogen.sh
code_of_conduct.md
compile
config.guess
config.h.in
config.rpath
config.sub
configure.REMOVED.git-id
configure.ac.REMOVED.git-id
depcomp
install-sh
ltmain.sh.REMOVED.git-id
missing
mkinstalldirs
mono-uninstalled.pc.in
test-driver
winconfig.h
linux-packaging-mono/external/llvm-project/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp

389 lines
13 KiB
C++
Raw Normal View History

//===--- ImplicitBoolConversionCheck.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 "ImplicitBoolConversionCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
#include "clang/Tooling/FixIt.h"
#include <queue>
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace readability {
namespace {
AST_MATCHER(Stmt, isMacroExpansion) {
SourceManager &SM = Finder->getASTContext().getSourceManager();
SourceLocation Loc = Node.getLocStart();
return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
}
bool isNULLMacroExpansion(const Stmt *Statement, ASTContext &Context) {
SourceManager &SM = Context.getSourceManager();
const LangOptions &LO = Context.getLangOpts();
SourceLocation Loc = Statement->getLocStart();
return SM.isMacroBodyExpansion(Loc) &&
Lexer::getImmediateMacroName(Loc, SM, LO) == "NULL";
}
AST_MATCHER(Stmt, isNULLMacroExpansion) {
return isNULLMacroExpansion(&Node, Finder->getASTContext());
}
StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
QualType Type,
ASTContext &Context) {
switch (CastExprKind) {
case CK_IntegralToBoolean:
return Type->isUnsignedIntegerType() ? "0u" : "0";
case CK_FloatingToBoolean:
return Context.hasSameType(Type, Context.FloatTy) ? "0.0f" : "0.0";
case CK_PointerToBoolean:
case CK_MemberPointerToBoolean: // Fall-through on purpose.
return Context.getLangOpts().CPlusPlus11 ? "nullptr" : "0";
default:
llvm_unreachable("Unexpected cast kind");
}
}
bool isUnaryLogicalNotOperator(const Stmt *Statement) {
const auto *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement);
return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;
}
bool areParensNeededForOverloadedOperator(OverloadedOperatorKind OperatorKind) {
switch (OperatorKind) {
case OO_New:
case OO_Delete: // Fall-through on purpose.
case OO_Array_New:
case OO_Array_Delete:
case OO_ArrowStar:
case OO_Arrow:
case OO_Call:
case OO_Subscript:
return false;
default:
return true;
}
}
bool areParensNeededForStatement(const Stmt *Statement) {
if (const auto *OperatorCall = dyn_cast<CXXOperatorCallExpr>(Statement)) {
return areParensNeededForOverloadedOperator(OperatorCall->getOperator());
}
return isa<BinaryOperator>(Statement) || isa<UnaryOperator>(Statement);
}
void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
const ImplicitCastExpr *Cast, const Stmt *Parent,
ASTContext &Context) {
// In case of expressions like (! integer), we should remove the redundant not
// operator and use inverted comparison (integer == 0).
bool InvertComparison =
Parent != nullptr && isUnaryLogicalNotOperator(Parent);
if (InvertComparison) {
SourceLocation ParentStartLoc = Parent->getLocStart();
SourceLocation ParentEndLoc =
cast<UnaryOperator>(Parent)->getSubExpr()->getLocStart();
Diag << FixItHint::CreateRemoval(
CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc));
Parent = Context.getParents(*Parent)[0].get<Stmt>();
}
const Expr *SubExpr = Cast->getSubExpr();
bool NeedInnerParens = areParensNeededForStatement(SubExpr);
bool NeedOuterParens =
Parent != nullptr && areParensNeededForStatement(Parent);
std::string StartLocInsertion;
if (NeedOuterParens) {
StartLocInsertion += "(";
}
if (NeedInnerParens) {
StartLocInsertion += "(";
}
if (!StartLocInsertion.empty()) {
Diag << FixItHint::CreateInsertion(Cast->getLocStart(), StartLocInsertion);
}
std::string EndLocInsertion;
if (NeedInnerParens) {
EndLocInsertion += ")";
}
if (InvertComparison) {
EndLocInsertion += " == ";
} else {
EndLocInsertion += " != ";
}
EndLocInsertion += getZeroLiteralToCompareWithForType(
Cast->getCastKind(), SubExpr->getType(), Context);
if (NeedOuterParens) {
EndLocInsertion += ")";
}
SourceLocation EndLoc = Lexer::getLocForEndOfToken(
Cast->getLocEnd(), 0, Context.getSourceManager(), Context.getLangOpts());
Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);
}
StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression,
ASTContext &Context) {
if (isNULLMacroExpansion(Expression, Context)) {
return "false";
}
if (const auto *IntLit = dyn_cast<IntegerLiteral>(Expression)) {
return (IntLit->getValue() == 0) ? "false" : "true";
}
if (const auto *FloatLit = dyn_cast<FloatingLiteral>(Expression)) {
llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
FloatLitAbsValue.clearSign();
return (FloatLitAbsValue.bitcastToAPInt() == 0) ? "false" : "true";
}
if (const auto *CharLit = dyn_cast<CharacterLiteral>(Expression)) {
return (CharLit->getValue() == 0) ? "false" : "true";
}
if (isa<StringLiteral>(Expression->IgnoreCasts())) {
return "true";
}
return StringRef();
}
void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
const ImplicitCastExpr *Cast,
ASTContext &Context, StringRef OtherType) {
const Expr *SubExpr = Cast->getSubExpr();
bool NeedParens = !isa<ParenExpr>(SubExpr);
Diag << FixItHint::CreateInsertion(
Cast->getLocStart(),
(Twine("static_cast<") + OtherType + ">" + (NeedParens ? "(" : ""))
.str());
if (NeedParens) {
SourceLocation EndLoc = Lexer::getLocForEndOfToken(
Cast->getLocEnd(), 0, Context.getSourceManager(),
Context.getLangOpts());
Diag << FixItHint::CreateInsertion(EndLoc, ")");
}
}
StringRef getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral,
QualType DestType, ASTContext &Context) {
// Prior to C++11, false literal could be implicitly converted to pointer.
if (!Context.getLangOpts().CPlusPlus11 &&
(DestType->isPointerType() || DestType->isMemberPointerType()) &&
BoolLiteral->getValue() == false) {
return "0";
}
if (DestType->isFloatingType()) {
if (Context.hasSameType(DestType, Context.FloatTy)) {
return BoolLiteral->getValue() ? "1.0f" : "0.0f";
}
return BoolLiteral->getValue() ? "1.0" : "0.0";
}
if (DestType->isUnsignedIntegerType()) {
return BoolLiteral->getValue() ? "1u" : "0u";
}
return BoolLiteral->getValue() ? "1" : "0";
}
bool isCastAllowedInCondition(const ImplicitCastExpr *Cast,
ASTContext &Context) {
std::queue<const Stmt *> Q;
Q.push(Cast);
while (!Q.empty()) {
for (const auto &N : Context.getParents(*Q.front())) {
const Stmt *S = N.get<Stmt>();
if (!S)
return false;
if (isa<IfStmt>(S) || isa<ConditionalOperator>(S) || isa<ForStmt>(S) ||
isa<WhileStmt>(S) || isa<BinaryConditionalOperator>(S))
return true;
if (isa<ParenExpr>(S) || isa<ImplicitCastExpr>(S) ||
isUnaryLogicalNotOperator(S) ||
(isa<BinaryOperator>(S) && cast<BinaryOperator>(S)->isLogicalOp())) {
Q.push(S);
} else {
return false;
}
}
Q.pop();
}
return false;
}
} // anonymous namespace
ImplicitBoolConversionCheck::ImplicitBoolConversionCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
AllowIntegerConditions(Options.get("AllowIntegerConditions", false)),
AllowPointerConditions(Options.get("AllowPointerConditions", false)) {}
void ImplicitBoolConversionCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "AllowIntegerConditions", AllowIntegerConditions);
Options.store(Opts, "AllowPointerConditions", AllowPointerConditions);
}
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) {
return;
}
auto exceptionCases =
expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
hasParent(explicitCastExpr())));
auto implicitCastFromBool = implicitCastExpr(
anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
// Prior to C++11 cast from bool literal to pointer was allowed.
allOf(anyOf(hasCastKind(CK_NullToPointer),
hasCastKind(CK_NullToMemberPointer)),
hasSourceExpression(cxxBoolLiteral()))),
hasSourceExpression(expr(hasType(booleanType()))),
unless(exceptionCases));
auto boolXor =
binaryOperator(hasOperatorName("^"), hasLHS(implicitCastFromBool),
hasRHS(implicitCastFromBool));
Finder->addMatcher(
implicitCastExpr(
anyOf(hasCastKind(CK_IntegralToBoolean),
hasCastKind(CK_FloatingToBoolean),
hasCastKind(CK_PointerToBoolean),
hasCastKind(CK_MemberPointerToBoolean)),
// Exclude case of using if or while statements with variable
// declaration, e.g.:
// if (int var = functionCall()) {}
unless(
hasParent(stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
// Exclude cases common to implicit cast to and from bool.
unless(exceptionCases), unless(has(boolXor)),
// Retrive also parent statement, to check if we need additional
// parens in replacement.
anyOf(hasParent(stmt().bind("parentStmt")), anything()),
unless(isInTemplateInstantiation()),
unless(hasAncestor(functionTemplateDecl())))
.bind("implicitCastToBool"),
this);
auto boolComparison = binaryOperator(
anyOf(hasOperatorName("=="), hasOperatorName("!=")),
hasLHS(implicitCastFromBool), hasRHS(implicitCastFromBool));
auto boolOpAssignment =
binaryOperator(anyOf(hasOperatorName("|="), hasOperatorName("&=")),
hasLHS(expr(hasType(booleanType()))));
Finder->addMatcher(
implicitCastExpr(
implicitCastFromBool,
// 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(boolComparison, boolXor, boolOpAssignment)))),
// Check also for nested casts, for example: bool -> int -> float.
anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
anything()),
unless(isInTemplateInstantiation()),
unless(hasAncestor(functionTemplateDecl())))
.bind("implicitCastFromBool"),
this);
}
void ImplicitBoolConversionCheck::check(
const MatchFinder::MatchResult &Result) {
if (const auto *CastToBool =
Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) {
const auto *Parent = Result.Nodes.getNodeAs<Stmt>("parentStmt");
return handleCastToBool(CastToBool, Parent, *Result.Context);
}
if (const auto *CastFromBool =
Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastFromBool")) {
const auto *NextImplicitCast =
Result.Nodes.getNodeAs<ImplicitCastExpr>("furtherImplicitCast");
return handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context);
}
}
void ImplicitBoolConversionCheck::handleCastToBool(const ImplicitCastExpr *Cast,
const Stmt *Parent,
ASTContext &Context) {
if (AllowPointerConditions &&
(Cast->getCastKind() == CK_PointerToBoolean ||
Cast->getCastKind() == CK_MemberPointerToBoolean) &&
isCastAllowedInCondition(Cast, Context)) {
return;
}
if (AllowIntegerConditions && Cast->getCastKind() == CK_IntegralToBoolean &&
isCastAllowedInCondition(Cast, Context)) {
return;
}
auto Diag = diag(Cast->getLocStart(), "implicit conversion %0 -> bool")
<< Cast->getSubExpr()->getType();
StringRef EquivalentLiteral =
getEquivalentBoolLiteralForExpr(Cast->getSubExpr(), Context);
if (!EquivalentLiteral.empty()) {
Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral);
} else {
fixGenericExprCastToBool(Diag, Cast, Parent, Context);
}
}
void ImplicitBoolConversionCheck::handleCastFromBool(
const ImplicitCastExpr *Cast, const ImplicitCastExpr *NextImplicitCast,
ASTContext &Context) {
QualType DestType =
NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
auto Diag = diag(Cast->getLocStart(), "implicit conversion bool -> %0")
<< DestType;
if (const auto *BoolLiteral =
dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr())) {
Diag << tooling::fixit::createReplacement(
*Cast, getEquivalentForBoolLiteral(BoolLiteral, DestType, Context));
} else {
fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString());
}
}
} // namespace readability
} // namespace tidy
} // namespace clang