11 #include "clang/Lex/Lexer.h"
17 using namespace clang::ast_matchers;
21 namespace readability {
25 StringRef getText(
const MatchFinder::MatchResult &
Result, SourceRange
Range) {
26 return Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
27 *Result.SourceManager,
28 Result.Context->getLangOpts());
32 StringRef getText(
const MatchFinder::MatchResult &Result, T &Node) {
33 return getText(Result, Node.getSourceRange());
36 const char RightExpressionId[] =
"bool-op-expr-yields-expr";
37 const char LeftExpressionId[] =
"expr-op-bool-yields-expr";
38 const char NegatedRightExpressionId[] =
"bool-op-expr-yields-not-expr";
39 const char NegatedLeftExpressionId[] =
"expr-op-bool-yields-not-expr";
40 const char ConditionThenStmtId[] =
"if-bool-yields-then";
41 const char ConditionElseStmtId[] =
"if-bool-yields-else";
42 const char TernaryId[] =
"ternary-bool-yields-condition";
43 const char TernaryNegatedId[] =
"ternary-bool-yields-not-condition";
44 const char IfReturnsBoolId[] =
"if-return";
45 const char IfReturnsNotBoolId[] =
"if-not-return";
46 const char ThenLiteralId[] =
"then-literal";
47 const char IfAssignVariableId[] =
"if-assign-lvalue";
48 const char IfAssignLocId[] =
"if-assign-loc";
49 const char IfAssignBoolId[] =
"if-assign";
50 const char IfAssignNotBoolId[] =
"if-assign-not";
51 const char IfAssignObjId[] =
"if-assign-obj";
52 const char CompoundReturnId[] =
"compound-return";
53 const char CompoundBoolId[] =
"compound-bool";
54 const char CompoundNotBoolId[] =
"compound-bool-not";
56 const char IfStmtId[] =
"if";
57 const char LHSId[] =
"lhs-expr";
58 const char RHSId[] =
"rhs-expr";
60 const char SimplifyOperatorDiagnostic[] =
61 "redundant boolean literal supplied to boolean operator";
62 const char SimplifyConditionDiagnostic[] =
63 "redundant boolean literal in if statement condition";
64 const char SimplifyConditionalReturnDiagnostic[] =
65 "redundant boolean literal in conditional return statement";
67 const CXXBoolLiteralExpr *getBoolLiteral(
const MatchFinder::MatchResult &Result,
69 const auto *Literal = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(Id);
71 Result.SourceManager->isMacroBodyExpansion(Literal->getLocStart()))
76 internal::Matcher<Stmt> returnsBool(
bool Value, StringRef Id =
"ignored") {
77 auto SimpleReturnsBool =
78 returnStmt(has(cxxBoolLiteral(equals(Value)).bind(Id)))
79 .bind(
"returns-bool");
80 return anyOf(SimpleReturnsBool,
81 compoundStmt(statementCountIs(1), has(SimpleReturnsBool)));
84 bool needsParensAfterUnaryNegation(
const Expr *E) {
85 E = E->IgnoreImpCasts();
86 if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E))
89 if (
const auto *Op = dyn_cast<CXXOperatorCallExpr>(E))
90 return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
91 Op->getOperator() != OO_Subscript;
96 std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = {
97 {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
99 StringRef negatedOperator(
const BinaryOperator *BinOp) {
100 const BinaryOperatorKind Opcode = BinOp->getOpcode();
101 for (
auto NegatableOp : Opposites) {
102 if (Opcode == NegatableOp.first)
103 return BinOp->getOpcodeStr(NegatableOp.second);
104 if (Opcode == NegatableOp.second)
105 return BinOp->getOpcodeStr(NegatableOp.first);
110 std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = {
111 {OO_EqualEqual,
"=="}, {OO_ExclaimEqual,
"!="}, {OO_Less,
"<"},
112 {OO_GreaterEqual,
">="}, {OO_Greater,
">"}, {OO_LessEqual,
"<="}};
114 StringRef getOperatorName(OverloadedOperatorKind OpKind) {
115 for (
auto Name : OperatorNames) {
116 if (
Name.first == OpKind)
123 std::pair<OverloadedOperatorKind, OverloadedOperatorKind> OppositeOverloads[] =
124 {{OO_EqualEqual, OO_ExclaimEqual},
125 {OO_Less, OO_GreaterEqual},
126 {OO_Greater, OO_LessEqual}};
128 StringRef negatedOperator(
const CXXOperatorCallExpr *OpCall) {
129 const OverloadedOperatorKind Opcode = OpCall->getOperator();
130 for (
auto NegatableOp : OppositeOverloads) {
131 if (Opcode == NegatableOp.first)
132 return getOperatorName(NegatableOp.second);
133 if (Opcode == NegatableOp.second)
134 return getOperatorName(NegatableOp.first);
139 std::string asBool(StringRef text,
bool NeedsStaticCast) {
141 return (
"static_cast<bool>(" + text +
")").str();
146 bool needsNullPtrComparison(
const Expr *E) {
147 if (
const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
148 return ImpCast->getCastKind() == CK_PointerToBoolean ||
149 ImpCast->getCastKind() == CK_MemberPointerToBoolean;
154 bool needsZeroComparison(
const Expr *E) {
155 if (
const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
156 return ImpCast->getCastKind() == CK_IntegralToBoolean;
161 bool needsStaticCast(
const Expr *E) {
162 if (
const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {
163 if (ImpCast->getCastKind() == CK_UserDefinedConversion &&
164 ImpCast->getSubExpr()->getType()->isBooleanType()) {
165 if (
const auto *MemCall =
166 dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) {
167 if (
const auto *MemDecl =
168 dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) {
169 if (MemDecl->isExplicit())
176 E = E->IgnoreImpCasts();
177 return !E->getType()->isBooleanType();
180 std::string compareExpressionToConstant(
const MatchFinder::MatchResult &Result,
181 const Expr *E,
bool Negated,
182 const char *Constant) {
183 E = E->IgnoreImpCasts();
184 const std::string ExprText =
185 (isa<BinaryOperator>(E) ? (
"(" + getText(Result, *E) +
")")
186 : getText(Result, *E))
188 return ExprText +
" " + (Negated ?
"!=" :
"==") +
" " + Constant;
191 std::string compareExpressionToNullPtr(
const MatchFinder::MatchResult &Result,
192 const Expr *E,
bool Negated) {
193 const char *NullPtr =
194 Result.Context->getLangOpts().CPlusPlus11 ?
"nullptr" :
"NULL";
195 return compareExpressionToConstant(Result, E, Negated, NullPtr);
198 std::string compareExpressionToZero(
const MatchFinder::MatchResult &Result,
199 const Expr *E,
bool Negated) {
200 return compareExpressionToConstant(Result, E, Negated,
"0");
203 std::string replacementExpression(
const MatchFinder::MatchResult &Result,
204 bool Negated,
const Expr *E) {
205 E = E->ignoreParenBaseCasts();
206 const bool NeedsStaticCast = needsStaticCast(E);
208 if (
const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
209 if (UnOp->getOpcode() == UO_LNot) {
210 if (needsNullPtrComparison(UnOp->getSubExpr()))
211 return compareExpressionToNullPtr(Result, UnOp->getSubExpr(),
true);
213 if (needsZeroComparison(UnOp->getSubExpr()))
214 return compareExpressionToZero(Result, UnOp->getSubExpr(),
true);
216 return replacementExpression(Result,
false, UnOp->getSubExpr());
220 if (needsNullPtrComparison(E))
221 return compareExpressionToNullPtr(Result, E,
false);
223 if (needsZeroComparison(E))
224 return compareExpressionToZero(Result, E,
false);
226 StringRef NegatedOperator;
227 const Expr *LHS =
nullptr;
228 const Expr *RHS =
nullptr;
229 if (
const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
230 NegatedOperator = negatedOperator(BinOp);
231 LHS = BinOp->getLHS();
232 RHS = BinOp->getRHS();
233 }
else if (
const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
234 if (OpExpr->getNumArgs() == 2) {
235 NegatedOperator = negatedOperator(OpExpr);
236 LHS = OpExpr->getArg(0);
237 RHS = OpExpr->getArg(1);
240 if (!NegatedOperator.empty() && LHS && RHS)
241 return (asBool((getText(Result, *LHS) +
" " + NegatedOperator +
" " +
242 getText(Result, *RHS))
246 StringRef Text = getText(Result, *E);
247 if (!NeedsStaticCast && needsParensAfterUnaryNegation(E))
248 return (
"!(" + Text +
")").str();
250 if (needsNullPtrComparison(E))
251 return compareExpressionToNullPtr(Result, E,
false);
253 if (needsZeroComparison(E))
254 return compareExpressionToZero(Result, E,
false);
256 return (
"!" + asBool(Text, NeedsStaticCast));
259 if (
const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
260 if (UnOp->getOpcode() == UO_LNot) {
261 if (needsNullPtrComparison(UnOp->getSubExpr()))
262 return compareExpressionToNullPtr(Result, UnOp->getSubExpr(),
false);
264 if (needsZeroComparison(UnOp->getSubExpr()))
265 return compareExpressionToZero(Result, UnOp->getSubExpr(),
false);
269 if (needsNullPtrComparison(E))
270 return compareExpressionToNullPtr(Result, E,
true);
272 if (needsZeroComparison(E))
273 return compareExpressionToZero(Result, E,
true);
275 return asBool(getText(Result, *E), NeedsStaticCast);
278 const CXXBoolLiteralExpr *stmtReturnsBool(
const ReturnStmt *Ret,
bool Negated) {
279 if (
const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) {
280 if (Bool->getValue() == !Negated)
287 const CXXBoolLiteralExpr *stmtReturnsBool(
const IfStmt *IfRet,
bool Negated) {
288 if (IfRet->getElse() !=
nullptr)
291 if (
const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen()))
292 return stmtReturnsBool(Ret, Negated);
294 if (
const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) {
295 if (Compound->size() == 1) {
296 if (
const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back()))
297 return stmtReturnsBool(CompoundRet, Negated);
304 bool containsDiscardedTokens(
const MatchFinder::MatchResult &Result,
305 CharSourceRange CharRange) {
306 std::string ReplacementText =
307 Lexer::getSourceText(CharRange, *Result.SourceManager,
308 Result.Context->getLangOpts())
310 Lexer Lex(CharRange.getBegin(), Result.Context->getLangOpts(),
311 ReplacementText.data(), ReplacementText.data(),
312 ReplacementText.data() + ReplacementText.size());
313 Lex.SetCommentRetentionState(
true);
316 while (!Lex.LexFromRawLexer(Tok)) {
317 if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash))
326 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef
Name,
329 ChainedConditionalReturn(Options.get(
"ChainedConditionalReturn", 0U)),
330 ChainedConditionalAssignment(
331 Options.get(
"ChainedConditionalAssignment", 0U)) {}
333 void SimplifyBooleanExprCheck::matchBoolBinOpExpr(MatchFinder *
Finder,
335 StringRef OperatorName,
336 StringRef BooleanId) {
339 isExpansionInMainFile(), hasOperatorName(OperatorName),
340 hasLHS(allOf(expr().bind(LHSId),
341 cxxBoolLiteral(equals(Value)).bind(BooleanId))),
342 hasRHS(expr().bind(RHSId)),
343 unless(hasRHS(hasDescendant(cxxBoolLiteral())))),
347 void SimplifyBooleanExprCheck::matchExprBinOpBool(MatchFinder *Finder,
349 StringRef OperatorName,
350 StringRef BooleanId) {
353 isExpansionInMainFile(), hasOperatorName(OperatorName),
354 hasLHS(expr().bind(LHSId)),
356 hasLHS(anyOf(cxxBoolLiteral(), hasDescendant(cxxBoolLiteral())))),
357 hasRHS(allOf(expr().bind(RHSId),
358 cxxBoolLiteral(equals(Value)).bind(BooleanId)))),
362 void SimplifyBooleanExprCheck::matchBoolCompOpExpr(MatchFinder *Finder,
364 StringRef OperatorName,
365 StringRef BooleanId) {
368 isExpansionInMainFile(), hasOperatorName(OperatorName),
371 ignoringImpCasts(cxxBoolLiteral(equals(Value)).bind(BooleanId)))),
372 hasRHS(expr().bind(RHSId)),
373 unless(hasRHS(hasDescendant(cxxBoolLiteral())))),
377 void SimplifyBooleanExprCheck::matchExprCompOpBool(MatchFinder *Finder,
379 StringRef OperatorName,
380 StringRef BooleanId) {
383 isExpansionInMainFile(), hasOperatorName(OperatorName),
384 unless(hasLHS(hasDescendant(cxxBoolLiteral()))),
385 hasLHS(expr().bind(LHSId)),
386 hasRHS(allOf(expr().bind(RHSId),
388 cxxBoolLiteral(equals(Value)).bind(BooleanId))))),
392 void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder,
394 StringRef BooleanId) {
396 ifStmt(isExpansionInMainFile(),
397 hasCondition(cxxBoolLiteral(equals(Value)).bind(BooleanId)))
402 void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder,
404 StringRef TernaryId) {
406 conditionalOperator(isExpansionInMainFile(),
407 hasTrueExpression(cxxBoolLiteral(equals(Value))),
408 hasFalseExpression(cxxBoolLiteral(equals(!Value))))
413 void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder,
414 bool Value, StringRef Id) {
415 if (ChainedConditionalReturn)
416 Finder->addMatcher(ifStmt(isExpansionInMainFile(),
417 hasThen(returnsBool(Value, ThenLiteralId)),
418 hasElse(returnsBool(!Value)))
422 Finder->addMatcher(ifStmt(isExpansionInMainFile(),
423 unless(hasParent(ifStmt())),
424 hasThen(returnsBool(Value, ThenLiteralId)),
425 hasElse(returnsBool(!Value)))
430 void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder,
431 bool Value, StringRef Id) {
432 auto SimpleThen = binaryOperator(
433 hasOperatorName(
"="),
434 hasLHS(declRefExpr(hasDeclaration(decl().bind(IfAssignObjId)))),
435 hasLHS(expr().bind(IfAssignVariableId)),
436 hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId)));
437 auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1),
438 hasAnySubstatement(SimpleThen)));
439 auto SimpleElse = binaryOperator(
440 hasOperatorName(
"="),
441 hasLHS(declRefExpr(hasDeclaration(equalsBoundNode(IfAssignObjId)))),
442 hasRHS(cxxBoolLiteral(equals(!Value))));
443 auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
444 hasAnySubstatement(SimpleElse)));
445 if (ChainedConditionalAssignment)
447 ifStmt(isExpansionInMainFile(), hasThen(Then), hasElse(Else)).bind(Id),
450 Finder->addMatcher(ifStmt(isExpansionInMainFile(),
451 unless(hasParent(ifStmt())), hasThen(Then),
457 void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
461 compoundStmt(allOf(hasAnySubstatement(ifStmt(hasThen(returnsBool(Value)),
462 unless(hasElse(stmt())))),
464 returnStmt(has(ignoringParenImpCasts(
465 cxxBoolLiteral(equals(!Value)))))
466 .bind(CompoundReturnId))))
472 Options.
store(Opts,
"ChainedConditionalReturn", ChainedConditionalReturn);
474 ChainedConditionalAssignment);
478 matchBoolBinOpExpr(Finder,
true,
"&&", RightExpressionId);
479 matchBoolBinOpExpr(Finder,
false,
"||", RightExpressionId);
480 matchExprBinOpBool(Finder,
false,
"&&", RightExpressionId);
481 matchExprBinOpBool(Finder,
true,
"||", RightExpressionId);
482 matchBoolCompOpExpr(Finder,
true,
"==", RightExpressionId);
483 matchBoolCompOpExpr(Finder,
false,
"!=", RightExpressionId);
485 matchExprBinOpBool(Finder,
true,
"&&", LeftExpressionId);
486 matchExprBinOpBool(Finder,
false,
"||", LeftExpressionId);
487 matchBoolBinOpExpr(Finder,
false,
"&&", LeftExpressionId);
488 matchBoolBinOpExpr(Finder,
true,
"||", LeftExpressionId);
489 matchExprCompOpBool(Finder,
true,
"==", LeftExpressionId);
490 matchExprCompOpBool(Finder,
false,
"!=", LeftExpressionId);
492 matchBoolCompOpExpr(Finder,
false,
"==", NegatedRightExpressionId);
493 matchBoolCompOpExpr(Finder,
true,
"!=", NegatedRightExpressionId);
495 matchExprCompOpBool(Finder,
false,
"==", NegatedLeftExpressionId);
496 matchExprCompOpBool(Finder,
true,
"!=", NegatedLeftExpressionId);
498 matchBoolCondition(Finder,
true, ConditionThenStmtId);
499 matchBoolCondition(Finder,
false, ConditionElseStmtId);
501 matchTernaryResult(Finder,
true, TernaryId);
502 matchTernaryResult(Finder,
false, TernaryNegatedId);
504 matchIfReturnsBool(Finder,
true, IfReturnsBoolId);
505 matchIfReturnsBool(Finder,
false, IfReturnsNotBoolId);
507 matchIfAssignsBool(Finder,
true, IfAssignBoolId);
508 matchIfAssignsBool(Finder,
false, IfAssignNotBoolId);
510 matchCompoundIfReturnsBool(Finder,
true, CompoundBoolId);
511 matchCompoundIfReturnsBool(Finder,
false, CompoundNotBoolId);
515 if (
const CXXBoolLiteralExpr *LeftRemoved =
516 getBoolLiteral(Result, RightExpressionId))
517 replaceWithExpression(Result, LeftRemoved,
false);
518 else if (
const CXXBoolLiteralExpr *RightRemoved =
519 getBoolLiteral(Result, LeftExpressionId))
520 replaceWithExpression(Result, RightRemoved,
true);
521 else if (
const CXXBoolLiteralExpr *NegatedLeftRemoved =
522 getBoolLiteral(Result, NegatedRightExpressionId))
523 replaceWithExpression(Result, NegatedLeftRemoved,
false,
true);
524 else if (
const CXXBoolLiteralExpr *NegatedRightRemoved =
525 getBoolLiteral(Result, NegatedLeftExpressionId))
526 replaceWithExpression(Result, NegatedRightRemoved,
true,
true);
527 else if (
const CXXBoolLiteralExpr *TrueConditionRemoved =
528 getBoolLiteral(Result, ConditionThenStmtId))
529 replaceWithThenStatement(Result, TrueConditionRemoved);
530 else if (
const CXXBoolLiteralExpr *FalseConditionRemoved =
531 getBoolLiteral(Result, ConditionElseStmtId))
532 replaceWithElseStatement(Result, FalseConditionRemoved);
533 else if (
const auto *Ternary =
534 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId))
535 replaceWithCondition(Result, Ternary);
536 else if (
const auto *TernaryNegated =
537 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId))
538 replaceWithCondition(Result, TernaryNegated,
true);
539 else if (
const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId))
540 replaceWithReturnCondition(Result, If);
541 else if (
const auto *IfNot =
542 Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId))
543 replaceWithReturnCondition(Result, IfNot,
true);
544 else if (
const auto *IfAssign =
545 Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId))
546 replaceWithAssignment(Result, IfAssign);
547 else if (
const auto *IfAssignNot =
548 Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId))
549 replaceWithAssignment(Result, IfAssignNot,
true);
550 else if (
const auto *Compound =
551 Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId))
552 replaceCompoundReturnWithCondition(Result, Compound);
553 else if (
const auto *Compound =
554 Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId))
555 replaceCompoundReturnWithCondition(Result, Compound,
true);
558 void SimplifyBooleanExprCheck::issueDiag(
559 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation
Loc,
560 StringRef Description, SourceRange ReplacementRange,
561 StringRef Replacement) {
562 CharSourceRange CharRange =
563 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange),
566 DiagnosticBuilder Diag =
diag(Loc, Description);
567 if (!containsDiscardedTokens(Result, CharRange))
568 Diag << FixItHint::CreateReplacement(CharRange, Replacement);
571 void SimplifyBooleanExprCheck::replaceWithExpression(
572 const ast_matchers::MatchFinder::MatchResult &Result,
573 const CXXBoolLiteralExpr *BoolLiteral,
bool UseLHS,
bool Negated) {
574 const auto *LHS = Result.Nodes.getNodeAs<Expr>(LHSId);
575 const auto *RHS = Result.Nodes.getNodeAs<Expr>(RHSId);
576 std::string Replacement =
577 replacementExpression(Result, Negated, UseLHS ? LHS : RHS);
578 SourceRange
Range(LHS->getLocStart(), RHS->getLocEnd());
579 issueDiag(Result, BoolLiteral->getLocStart(), SimplifyOperatorDiagnostic,
583 void SimplifyBooleanExprCheck::replaceWithThenStatement(
584 const MatchFinder::MatchResult &Result,
585 const CXXBoolLiteralExpr *TrueConditionRemoved) {
586 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
587 issueDiag(Result, TrueConditionRemoved->getLocStart(),
588 SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
589 getText(Result, *IfStatement->getThen()));
592 void SimplifyBooleanExprCheck::replaceWithElseStatement(
593 const MatchFinder::MatchResult &Result,
594 const CXXBoolLiteralExpr *FalseConditionRemoved) {
595 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
596 const Stmt *ElseStatement = IfStatement->getElse();
597 issueDiag(Result, FalseConditionRemoved->getLocStart(),
598 SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
599 ElseStatement ? getText(Result, *ElseStatement) :
"");
602 void SimplifyBooleanExprCheck::replaceWithCondition(
603 const MatchFinder::MatchResult &Result,
const ConditionalOperator *Ternary,
605 std::string Replacement =
606 replacementExpression(Result, Negated, Ternary->getCond());
607 issueDiag(Result, Ternary->getTrueExpr()->getLocStart(),
608 "redundant boolean literal in ternary expression result",
609 Ternary->getSourceRange(), Replacement);
612 void SimplifyBooleanExprCheck::replaceWithReturnCondition(
613 const MatchFinder::MatchResult &Result,
const IfStmt *If,
bool Negated) {
614 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ?
";" :
"";
615 std::string Condition = replacementExpression(Result, Negated, If->getCond());
616 std::string Replacement = (
"return " + Condition + Terminator).str();
617 SourceLocation Start =
618 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getLocStart();
619 issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic,
620 If->getSourceRange(), Replacement);
623 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
624 const MatchFinder::MatchResult &Result,
const CompoundStmt *Compound,
626 const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId);
634 assert(Compound->size() >= 2);
635 const IfStmt *BeforeIf =
nullptr;
636 CompoundStmt::const_body_iterator Current = Compound->body_begin();
637 CompoundStmt::const_body_iterator After = Compound->body_begin();
638 for (++After; After != Compound->body_end() && *Current != Ret;
639 ++Current, ++After) {
640 if (
const auto *If = dyn_cast<IfStmt>(*Current)) {
641 if (
const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) {
643 if (!ChainedConditionalReturn && BeforeIf)
646 const Expr *Condition = If->getCond();
647 std::string Replacement =
648 "return " + replacementExpression(Result, Negated, Condition);
650 Result, Lit->getLocStart(), SimplifyConditionalReturnDiagnostic,
651 SourceRange(If->getLocStart(), Ret->getLocEnd()), Replacement);
663 void SimplifyBooleanExprCheck::replaceWithAssignment(
664 const MatchFinder::MatchResult &Result,
const IfStmt *IfAssign,
666 SourceRange Range = IfAssign->getSourceRange();
667 StringRef VariableName =
668 getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId));
669 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ?
";" :
"";
670 std::string Condition =
671 replacementExpression(Result, Negated, IfAssign->getCond());
672 std::string Replacement =
673 (VariableName +
" = " + Condition + Terminator).str();
675 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getLocStart();
676 issueDiag(Result, Location,
677 "redundant boolean literal in conditional assignment", Range,
SourceLocation Loc
'#' location in the include directive
LangOptions getLangOpts() const
Returns the language options from the context.
std::unique_ptr< ast_matchers::MatchFinder > Finder
void storeOptions(ClangTidyOptions::OptionMap &Options) override
Should store all options supported by this check with their current values or default values for opti...
Base class for all clang-tidy checks.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
std::map< std::string, std::string > OptionMap
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
CharSourceRange Range
SourceRange for the file name.
ClangTidyContext & Context
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.