llvm-project/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
Chandler Carruth 2946cd7010 Update the file headers across all of the LLVM projects in the monorepo
to reflect the new license.

We understand that people may be surprised that we're moving the header
entirely to discuss the new license. We checked this carefully with the
Foundation's lawyer and we believe this is the correct approach.

Essentially, all code in the project is now made available by the LLVM
project under our new license, so you will see that the license headers
include that license only. Some of our contributors have contributed
code under our old license, and accordingly, we have retained a copy of
our old license notice in the top-level files in each project and
repository.

llvm-svn: 351636
2019-01-19 08:50:56 +00:00

674 lines
25 KiB
C++

//===--- SimplifyBooleanExpr.cpp clang-tidy ---------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "SimplifyBooleanExprCheck.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Lex/Lexer.h"
#include <cassert>
#include <string>
#include <utility>
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace readability {
namespace {
StringRef getText(const MatchFinder::MatchResult &Result, SourceRange Range) {
return Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
*Result.SourceManager,
Result.Context->getLangOpts());
}
template <typename T>
StringRef getText(const MatchFinder::MatchResult &Result, T &Node) {
return getText(Result, Node.getSourceRange());
}
const char ConditionThenStmtId[] = "if-bool-yields-then";
const char ConditionElseStmtId[] = "if-bool-yields-else";
const char TernaryId[] = "ternary-bool-yields-condition";
const char TernaryNegatedId[] = "ternary-bool-yields-not-condition";
const char IfReturnsBoolId[] = "if-return";
const char IfReturnsNotBoolId[] = "if-not-return";
const char ThenLiteralId[] = "then-literal";
const char IfAssignVariableId[] = "if-assign-lvalue";
const char IfAssignLocId[] = "if-assign-loc";
const char IfAssignBoolId[] = "if-assign";
const char IfAssignNotBoolId[] = "if-assign-not";
const char IfAssignObjId[] = "if-assign-obj";
const char CompoundReturnId[] = "compound-return";
const char CompoundBoolId[] = "compound-bool";
const char CompoundNotBoolId[] = "compound-bool-not";
const char IfStmtId[] = "if";
const char SimplifyOperatorDiagnostic[] =
"redundant boolean literal supplied to boolean operator";
const char SimplifyConditionDiagnostic[] =
"redundant boolean literal in if statement condition";
const char SimplifyConditionalReturnDiagnostic[] =
"redundant boolean literal in conditional return statement";
const CXXBoolLiteralExpr *getBoolLiteral(const MatchFinder::MatchResult &Result,
StringRef Id) {
const auto *Literal = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(Id);
return (Literal && Literal->getBeginLoc().isMacroID()) ? nullptr : Literal;
}
internal::Matcher<Stmt> returnsBool(bool Value, StringRef Id = "ignored") {
auto SimpleReturnsBool =
returnStmt(has(cxxBoolLiteral(equals(Value)).bind(Id)))
.bind("returns-bool");
return anyOf(SimpleReturnsBool,
compoundStmt(statementCountIs(1), has(SimpleReturnsBool)));
}
bool needsParensAfterUnaryNegation(const Expr *E) {
E = E->IgnoreImpCasts();
if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E))
return true;
if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E))
return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
Op->getOperator() != OO_Subscript;
return false;
}
std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = {
{BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
StringRef negatedOperator(const BinaryOperator *BinOp) {
const BinaryOperatorKind Opcode = BinOp->getOpcode();
for (auto NegatableOp : Opposites) {
if (Opcode == NegatableOp.first)
return BinOp->getOpcodeStr(NegatableOp.second);
if (Opcode == NegatableOp.second)
return BinOp->getOpcodeStr(NegatableOp.first);
}
return StringRef();
}
std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = {
{OO_EqualEqual, "=="}, {OO_ExclaimEqual, "!="}, {OO_Less, "<"},
{OO_GreaterEqual, ">="}, {OO_Greater, ">"}, {OO_LessEqual, "<="}};
StringRef getOperatorName(OverloadedOperatorKind OpKind) {
for (auto Name : OperatorNames) {
if (Name.first == OpKind)
return Name.second;
}
return StringRef();
}
std::pair<OverloadedOperatorKind, OverloadedOperatorKind> OppositeOverloads[] =
{{OO_EqualEqual, OO_ExclaimEqual},
{OO_Less, OO_GreaterEqual},
{OO_Greater, OO_LessEqual}};
StringRef negatedOperator(const CXXOperatorCallExpr *OpCall) {
const OverloadedOperatorKind Opcode = OpCall->getOperator();
for (auto NegatableOp : OppositeOverloads) {
if (Opcode == NegatableOp.first)
return getOperatorName(NegatableOp.second);
if (Opcode == NegatableOp.second)
return getOperatorName(NegatableOp.first);
}
return StringRef();
}
std::string asBool(StringRef text, bool NeedsStaticCast) {
if (NeedsStaticCast)
return ("static_cast<bool>(" + text + ")").str();
return text;
}
bool needsNullPtrComparison(const Expr *E) {
if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
return ImpCast->getCastKind() == CK_PointerToBoolean ||
ImpCast->getCastKind() == CK_MemberPointerToBoolean;
return false;
}
bool needsZeroComparison(const Expr *E) {
if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
return ImpCast->getCastKind() == CK_IntegralToBoolean;
return false;
}
bool needsStaticCast(const Expr *E) {
if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {
if (ImpCast->getCastKind() == CK_UserDefinedConversion &&
ImpCast->getSubExpr()->getType()->isBooleanType()) {
if (const auto *MemCall =
dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) {
if (const auto *MemDecl =
dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) {
if (MemDecl->isExplicit())
return true;
}
}
}
}
E = E->IgnoreImpCasts();
return !E->getType()->isBooleanType();
}
std::string compareExpressionToConstant(const MatchFinder::MatchResult &Result,
const Expr *E, bool Negated,
const char *Constant) {
E = E->IgnoreImpCasts();
const std::string ExprText =
(isa<BinaryOperator>(E) ? ("(" + getText(Result, *E) + ")")
: getText(Result, *E))
.str();
return ExprText + " " + (Negated ? "!=" : "==") + " " + Constant;
}
std::string compareExpressionToNullPtr(const MatchFinder::MatchResult &Result,
const Expr *E, bool Negated) {
const char *NullPtr =
Result.Context->getLangOpts().CPlusPlus11 ? "nullptr" : "NULL";
return compareExpressionToConstant(Result, E, Negated, NullPtr);
}
std::string compareExpressionToZero(const MatchFinder::MatchResult &Result,
const Expr *E, bool Negated) {
return compareExpressionToConstant(Result, E, Negated, "0");
}
std::string replacementExpression(const MatchFinder::MatchResult &Result,
bool Negated, const Expr *E) {
E = E->ignoreParenBaseCasts();
if (const auto *EC = dyn_cast<ExprWithCleanups>(E))
E = EC->getSubExpr();
const bool NeedsStaticCast = needsStaticCast(E);
if (Negated) {
if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
if (UnOp->getOpcode() == UO_LNot) {
if (needsNullPtrComparison(UnOp->getSubExpr()))
return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), true);
if (needsZeroComparison(UnOp->getSubExpr()))
return compareExpressionToZero(Result, UnOp->getSubExpr(), true);
return replacementExpression(Result, false, UnOp->getSubExpr());
}
}
if (needsNullPtrComparison(E))
return compareExpressionToNullPtr(Result, E, false);
if (needsZeroComparison(E))
return compareExpressionToZero(Result, E, false);
StringRef NegatedOperator;
const Expr *LHS = nullptr;
const Expr *RHS = nullptr;
if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
NegatedOperator = negatedOperator(BinOp);
LHS = BinOp->getLHS();
RHS = BinOp->getRHS();
} else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
if (OpExpr->getNumArgs() == 2) {
NegatedOperator = negatedOperator(OpExpr);
LHS = OpExpr->getArg(0);
RHS = OpExpr->getArg(1);
}
}
if (!NegatedOperator.empty() && LHS && RHS)
return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " +
getText(Result, *RHS))
.str(),
NeedsStaticCast));
StringRef Text = getText(Result, *E);
if (!NeedsStaticCast && needsParensAfterUnaryNegation(E))
return ("!(" + Text + ")").str();
if (needsNullPtrComparison(E))
return compareExpressionToNullPtr(Result, E, false);
if (needsZeroComparison(E))
return compareExpressionToZero(Result, E, false);
return ("!" + asBool(Text, NeedsStaticCast));
}
if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
if (UnOp->getOpcode() == UO_LNot) {
if (needsNullPtrComparison(UnOp->getSubExpr()))
return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), false);
if (needsZeroComparison(UnOp->getSubExpr()))
return compareExpressionToZero(Result, UnOp->getSubExpr(), false);
}
}
if (needsNullPtrComparison(E))
return compareExpressionToNullPtr(Result, E, true);
if (needsZeroComparison(E))
return compareExpressionToZero(Result, E, true);
return asBool(getText(Result, *E), NeedsStaticCast);
}
const CXXBoolLiteralExpr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) {
if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) {
if (Bool->getValue() == !Negated)
return Bool;
}
return nullptr;
}
const CXXBoolLiteralExpr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) {
if (IfRet->getElse() != nullptr)
return nullptr;
if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen()))
return stmtReturnsBool(Ret, Negated);
if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) {
if (Compound->size() == 1) {
if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back()))
return stmtReturnsBool(CompoundRet, Negated);
}
}
return nullptr;
}
bool containsDiscardedTokens(const MatchFinder::MatchResult &Result,
CharSourceRange CharRange) {
std::string ReplacementText =
Lexer::getSourceText(CharRange, *Result.SourceManager,
Result.Context->getLangOpts())
.str();
Lexer Lex(CharRange.getBegin(), Result.Context->getLangOpts(),
ReplacementText.data(), ReplacementText.data(),
ReplacementText.data() + ReplacementText.size());
Lex.SetCommentRetentionState(true);
Token Tok;
while (!Lex.LexFromRawLexer(Tok)) {
if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash))
return true;
}
return false;
}
} // namespace
class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
public:
Visitor(SimplifyBooleanExprCheck *Check,
const MatchFinder::MatchResult &Result)
: Check(Check), Result(Result) {}
bool VisitBinaryOperator(BinaryOperator *Op) {
Check->reportBinOp(Result, Op);
return true;
}
private:
SimplifyBooleanExprCheck *Check;
const MatchFinder::MatchResult &Result;
};
SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
ChainedConditionalReturn(Options.get("ChainedConditionalReturn", 0U)),
ChainedConditionalAssignment(
Options.get("ChainedConditionalAssignment", 0U)) {}
bool containsBoolLiteral(const Expr *E) {
if (!E)
return false;
E = E->IgnoreParenImpCasts();
if (isa<CXXBoolLiteralExpr>(E))
return true;
if (const auto *BinOp = dyn_cast<BinaryOperator>(E))
return containsBoolLiteral(BinOp->getLHS()) ||
containsBoolLiteral(BinOp->getRHS());
if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E))
return containsBoolLiteral(UnaryOp->getSubExpr());
return false;
}
void SimplifyBooleanExprCheck::reportBinOp(
const MatchFinder::MatchResult &Result, const BinaryOperator *Op) {
const auto *LHS = Op->getLHS()->IgnoreParenImpCasts();
const auto *RHS = Op->getRHS()->IgnoreParenImpCasts();
const CXXBoolLiteralExpr *Bool = nullptr;
const Expr *Other = nullptr;
if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS)))
Other = RHS;
else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS)))
Other = LHS;
else
return;
if (Bool->getBeginLoc().isMacroID())
return;
// FIXME: why do we need this?
if (!isa<CXXBoolLiteralExpr>(Other) && containsBoolLiteral(Other))
return;
bool BoolValue = Bool->getValue();
auto replaceWithExpression = [this, &Result, LHS, RHS, Bool](
const Expr *ReplaceWith, bool Negated) {
std::string Replacement =
replacementExpression(Result, Negated, ReplaceWith);
SourceRange Range(LHS->getBeginLoc(), RHS->getEndLoc());
issueDiag(Result, Bool->getBeginLoc(), SimplifyOperatorDiagnostic, Range,
Replacement);
};
switch (Op->getOpcode()) {
case BO_LAnd:
if (BoolValue) {
// expr && true -> expr
replaceWithExpression(Other, /*Negated=*/false);
} else {
// expr && false -> false
replaceWithExpression(Bool, /*Negated=*/false);
}
break;
case BO_LOr:
if (BoolValue) {
// expr || true -> true
replaceWithExpression(Bool, /*Negated=*/false);
} else {
// expr || false -> expr
replaceWithExpression(Other, /*Negated=*/false);
}
break;
case BO_EQ:
// expr == true -> expr, expr == false -> !expr
replaceWithExpression(Other, /*Negated=*/!BoolValue);
break;
case BO_NE:
// expr != true -> !expr, expr != false -> expr
replaceWithExpression(Other, /*Negated=*/BoolValue);
break;
default:
break;
}
}
void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder,
bool Value,
StringRef BooleanId) {
Finder->addMatcher(
ifStmt(isExpansionInMainFile(),
hasCondition(cxxBoolLiteral(equals(Value)).bind(BooleanId)))
.bind(IfStmtId),
this);
}
void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder,
bool Value,
StringRef TernaryId) {
Finder->addMatcher(
conditionalOperator(isExpansionInMainFile(),
hasTrueExpression(cxxBoolLiteral(equals(Value))),
hasFalseExpression(cxxBoolLiteral(equals(!Value))))
.bind(TernaryId),
this);
}
void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder,
bool Value, StringRef Id) {
if (ChainedConditionalReturn)
Finder->addMatcher(ifStmt(isExpansionInMainFile(),
hasThen(returnsBool(Value, ThenLiteralId)),
hasElse(returnsBool(!Value)))
.bind(Id),
this);
else
Finder->addMatcher(ifStmt(isExpansionInMainFile(),
unless(hasParent(ifStmt())),
hasThen(returnsBool(Value, ThenLiteralId)),
hasElse(returnsBool(!Value)))
.bind(Id),
this);
}
void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder,
bool Value, StringRef Id) {
auto SimpleThen = binaryOperator(
hasOperatorName("="),
hasLHS(declRefExpr(hasDeclaration(decl().bind(IfAssignObjId)))),
hasLHS(expr().bind(IfAssignVariableId)),
hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId)));
auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1),
hasAnySubstatement(SimpleThen)));
auto SimpleElse = binaryOperator(
hasOperatorName("="),
hasLHS(declRefExpr(hasDeclaration(equalsBoundNode(IfAssignObjId)))),
hasRHS(cxxBoolLiteral(equals(!Value))));
auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
hasAnySubstatement(SimpleElse)));
if (ChainedConditionalAssignment)
Finder->addMatcher(
ifStmt(isExpansionInMainFile(), hasThen(Then), hasElse(Else)).bind(Id),
this);
else
Finder->addMatcher(ifStmt(isExpansionInMainFile(),
unless(hasParent(ifStmt())), hasThen(Then),
hasElse(Else))
.bind(Id),
this);
}
void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
bool Value,
StringRef Id) {
Finder->addMatcher(
compoundStmt(
hasAnySubstatement(
ifStmt(hasThen(returnsBool(Value)), unless(hasElse(stmt())))),
hasAnySubstatement(returnStmt(has(ignoringParenImpCasts(
cxxBoolLiteral(equals(!Value)))))
.bind(CompoundReturnId)))
.bind(Id),
this);
}
void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
Options.store(Opts, "ChainedConditionalAssignment",
ChainedConditionalAssignment);
}
void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(translationUnitDecl().bind("top"), this);
matchBoolCondition(Finder, true, ConditionThenStmtId);
matchBoolCondition(Finder, false, ConditionElseStmtId);
matchTernaryResult(Finder, true, TernaryId);
matchTernaryResult(Finder, false, TernaryNegatedId);
matchIfReturnsBool(Finder, true, IfReturnsBoolId);
matchIfReturnsBool(Finder, false, IfReturnsNotBoolId);
matchIfAssignsBool(Finder, true, IfAssignBoolId);
matchIfAssignsBool(Finder, false, IfAssignNotBoolId);
matchCompoundIfReturnsBool(Finder, true, CompoundBoolId);
matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId);
}
void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
if (Result.Nodes.getNodeAs<TranslationUnitDecl>("top"))
Visitor(this, Result).TraverseAST(*Result.Context);
else if (const CXXBoolLiteralExpr *TrueConditionRemoved =
getBoolLiteral(Result, ConditionThenStmtId))
replaceWithThenStatement(Result, TrueConditionRemoved);
else if (const CXXBoolLiteralExpr *FalseConditionRemoved =
getBoolLiteral(Result, ConditionElseStmtId))
replaceWithElseStatement(Result, FalseConditionRemoved);
else if (const auto *Ternary =
Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId))
replaceWithCondition(Result, Ternary);
else if (const auto *TernaryNegated =
Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId))
replaceWithCondition(Result, TernaryNegated, true);
else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId))
replaceWithReturnCondition(Result, If);
else if (const auto *IfNot =
Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId))
replaceWithReturnCondition(Result, IfNot, true);
else if (const auto *IfAssign =
Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId))
replaceWithAssignment(Result, IfAssign);
else if (const auto *IfAssignNot =
Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId))
replaceWithAssignment(Result, IfAssignNot, true);
else if (const auto *Compound =
Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId))
replaceCompoundReturnWithCondition(Result, Compound);
else if (const auto *Compound =
Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId))
replaceCompoundReturnWithCondition(Result, Compound, true);
}
void SimplifyBooleanExprCheck::issueDiag(
const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc,
StringRef Description, SourceRange ReplacementRange,
StringRef Replacement) {
CharSourceRange CharRange =
Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange),
*Result.SourceManager, getLangOpts());
DiagnosticBuilder Diag = diag(Loc, Description);
if (!containsDiscardedTokens(Result, CharRange))
Diag << FixItHint::CreateReplacement(CharRange, Replacement);
}
void SimplifyBooleanExprCheck::replaceWithThenStatement(
const MatchFinder::MatchResult &Result,
const CXXBoolLiteralExpr *TrueConditionRemoved) {
const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
issueDiag(Result, TrueConditionRemoved->getBeginLoc(),
SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
getText(Result, *IfStatement->getThen()));
}
void SimplifyBooleanExprCheck::replaceWithElseStatement(
const MatchFinder::MatchResult &Result,
const CXXBoolLiteralExpr *FalseConditionRemoved) {
const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
const Stmt *ElseStatement = IfStatement->getElse();
issueDiag(Result, FalseConditionRemoved->getBeginLoc(),
SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
ElseStatement ? getText(Result, *ElseStatement) : "");
}
void SimplifyBooleanExprCheck::replaceWithCondition(
const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary,
bool Negated) {
std::string Replacement =
replacementExpression(Result, Negated, Ternary->getCond());
issueDiag(Result, Ternary->getTrueExpr()->getBeginLoc(),
"redundant boolean literal in ternary expression result",
Ternary->getSourceRange(), Replacement);
}
void SimplifyBooleanExprCheck::replaceWithReturnCondition(
const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) {
StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : "";
std::string Condition = replacementExpression(Result, Negated, If->getCond());
std::string Replacement = ("return " + Condition + Terminator).str();
SourceLocation Start =
Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getBeginLoc();
issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic,
If->getSourceRange(), Replacement);
}
void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
const MatchFinder::MatchResult &Result, const CompoundStmt *Compound,
bool Negated) {
const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId);
// The body shouldn't be empty because the matcher ensures that it must
// contain at least two statements:
// 1) A `return` statement returning a boolean literal `false` or `true`
// 2) An `if` statement with no `else` clause that consists of a single
// `return` statement returning the opposite boolean literal `true` or
// `false`.
assert(Compound->size() >= 2);
const IfStmt *BeforeIf = nullptr;
CompoundStmt::const_body_iterator Current = Compound->body_begin();
CompoundStmt::const_body_iterator After = Compound->body_begin();
for (++After; After != Compound->body_end() && *Current != Ret;
++Current, ++After) {
if (const auto *If = dyn_cast<IfStmt>(*Current)) {
if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) {
if (*After == Ret) {
if (!ChainedConditionalReturn && BeforeIf)
continue;
const Expr *Condition = If->getCond();
std::string Replacement =
"return " + replacementExpression(Result, Negated, Condition);
issueDiag(
Result, Lit->getBeginLoc(), SimplifyConditionalReturnDiagnostic,
SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement);
return;
}
BeforeIf = If;
}
} else {
BeforeIf = nullptr;
}
}
}
void SimplifyBooleanExprCheck::replaceWithAssignment(
const MatchFinder::MatchResult &Result, const IfStmt *IfAssign,
bool Negated) {
SourceRange Range = IfAssign->getSourceRange();
StringRef VariableName =
getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId));
StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : "";
std::string Condition =
replacementExpression(Result, Negated, IfAssign->getCond());
std::string Replacement =
(VariableName + " = " + Condition + Terminator).str();
SourceLocation Location =
Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getBeginLoc();
issueDiag(Result, Location,
"redundant boolean literal in conditional assignment", Range,
Replacement);
}
} // namespace readability
} // namespace tidy
} // namespace clang