10 #include "clang/AST/RecursiveASTVisitor.h"
11 #include "clang/Lex/Lexer.h"
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 ConditionThenStmtId[] =
"if-bool-yields-then";
37 const char ConditionElseStmtId[] =
"if-bool-yields-else";
38 const char TernaryId[] =
"ternary-bool-yields-condition";
39 const char TernaryNegatedId[] =
"ternary-bool-yields-not-condition";
40 const char IfReturnsBoolId[] =
"if-return";
41 const char IfReturnsNotBoolId[] =
"if-not-return";
42 const char ThenLiteralId[] =
"then-literal";
43 const char IfAssignVariableId[] =
"if-assign-lvalue";
44 const char IfAssignLocId[] =
"if-assign-loc";
45 const char IfAssignBoolId[] =
"if-assign";
46 const char IfAssignNotBoolId[] =
"if-assign-not";
47 const char IfAssignVarId[] =
"if-assign-var";
48 const char CompoundReturnId[] =
"compound-return";
49 const char CompoundBoolId[] =
"compound-bool";
50 const char CompoundNotBoolId[] =
"compound-bool-not";
52 const char IfStmtId[] =
"if";
54 const char SimplifyOperatorDiagnostic[] =
55 "redundant boolean literal supplied to boolean operator";
56 const char SimplifyConditionDiagnostic[] =
57 "redundant boolean literal in if statement condition";
58 const char SimplifyConditionalReturnDiagnostic[] =
59 "redundant boolean literal in conditional return statement";
61 const CXXBoolLiteralExpr *getBoolLiteral(
const MatchFinder::MatchResult &Result,
63 const auto *Literal = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(Id);
64 return (Literal && Literal->getBeginLoc().isMacroID()) ?
nullptr : Literal;
67 internal::Matcher<Stmt> returnsBool(
bool Value, StringRef Id =
"ignored") {
68 auto SimpleReturnsBool =
69 returnStmt(has(cxxBoolLiteral(equals(Value)).bind(Id)))
70 .bind(
"returns-bool");
71 return anyOf(SimpleReturnsBool,
72 compoundStmt(statementCountIs(1), has(SimpleReturnsBool)));
75 bool needsParensAfterUnaryNegation(
const Expr *
E) {
76 E =
E->IgnoreImpCasts();
77 if (isa<BinaryOperator>(
E) || isa<ConditionalOperator>(
E))
80 if (
const auto *Op = dyn_cast<CXXOperatorCallExpr>(
E))
81 return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
82 Op->getOperator() != OO_Subscript;
87 std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = {
88 {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
90 StringRef negatedOperator(
const BinaryOperator *BinOp) {
91 const BinaryOperatorKind Opcode = BinOp->getOpcode();
92 for (
auto NegatableOp : Opposites) {
93 if (Opcode == NegatableOp.first)
94 return BinOp->getOpcodeStr(NegatableOp.second);
95 if (Opcode == NegatableOp.second)
96 return BinOp->getOpcodeStr(NegatableOp.first);
101 std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = {
102 {OO_EqualEqual,
"=="}, {OO_ExclaimEqual,
"!="}, {OO_Less,
"<"},
103 {OO_GreaterEqual,
">="}, {OO_Greater,
">"}, {OO_LessEqual,
"<="}};
105 StringRef getOperatorName(OverloadedOperatorKind OpKind) {
106 for (
auto Name : OperatorNames) {
107 if (
Name.first == OpKind)
114 std::pair<OverloadedOperatorKind, OverloadedOperatorKind> OppositeOverloads[] =
115 {{OO_EqualEqual, OO_ExclaimEqual},
116 {OO_Less, OO_GreaterEqual},
117 {OO_Greater, OO_LessEqual}};
119 StringRef negatedOperator(
const CXXOperatorCallExpr *OpCall) {
120 const OverloadedOperatorKind Opcode = OpCall->getOperator();
121 for (
auto NegatableOp : OppositeOverloads) {
122 if (Opcode == NegatableOp.first)
123 return getOperatorName(NegatableOp.second);
124 if (Opcode == NegatableOp.second)
125 return getOperatorName(NegatableOp.first);
130 std::string asBool(StringRef text,
bool NeedsStaticCast) {
132 return (
"static_cast<bool>(" + text +
")").str();
134 return std::string(text);
137 bool needsNullPtrComparison(
const Expr *
E) {
138 if (
const auto *ImpCast = dyn_cast<ImplicitCastExpr>(
E))
139 return ImpCast->getCastKind() == CK_PointerToBoolean ||
140 ImpCast->getCastKind() == CK_MemberPointerToBoolean;
145 bool needsZeroComparison(
const Expr *
E) {
146 if (
const auto *ImpCast = dyn_cast<ImplicitCastExpr>(
E))
147 return ImpCast->getCastKind() == CK_IntegralToBoolean;
152 bool needsStaticCast(
const Expr *
E) {
153 if (
const auto *ImpCast = dyn_cast<ImplicitCastExpr>(
E)) {
154 if (ImpCast->getCastKind() == CK_UserDefinedConversion &&
155 ImpCast->getSubExpr()->getType()->isBooleanType()) {
156 if (
const auto *MemCall =
157 dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) {
158 if (
const auto *MemDecl =
159 dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) {
160 if (MemDecl->isExplicit())
167 E =
E->IgnoreImpCasts();
168 return !
E->getType()->isBooleanType();
171 std::string compareExpressionToConstant(
const MatchFinder::MatchResult &Result,
172 const Expr *
E,
bool Negated,
173 const char *Constant) {
174 E =
E->IgnoreImpCasts();
175 const std::string ExprText =
176 (isa<BinaryOperator>(
E) ? (
"(" + getText(Result, *
E) +
")")
177 : getText(Result, *
E))
179 return ExprText +
" " + (Negated ?
"!=" :
"==") +
" " + Constant;
182 std::string compareExpressionToNullPtr(
const MatchFinder::MatchResult &Result,
183 const Expr *
E,
bool Negated) {
184 const char *NullPtr =
185 Result.Context->getLangOpts().CPlusPlus11 ?
"nullptr" :
"NULL";
186 return compareExpressionToConstant(Result,
E, Negated, NullPtr);
189 std::string compareExpressionToZero(
const MatchFinder::MatchResult &Result,
190 const Expr *
E,
bool Negated) {
191 return compareExpressionToConstant(Result,
E, Negated,
"0");
194 std::string replacementExpression(
const MatchFinder::MatchResult &Result,
195 bool Negated,
const Expr *
E) {
196 E =
E->ignoreParenBaseCasts();
197 if (
const auto *EC = dyn_cast<ExprWithCleanups>(
E))
198 E = EC->getSubExpr();
200 const bool NeedsStaticCast = needsStaticCast(
E);
202 if (
const auto *UnOp = dyn_cast<UnaryOperator>(
E)) {
203 if (UnOp->getOpcode() == UO_LNot) {
204 if (needsNullPtrComparison(UnOp->getSubExpr()))
205 return compareExpressionToNullPtr(Result, UnOp->getSubExpr(),
true);
207 if (needsZeroComparison(UnOp->getSubExpr()))
208 return compareExpressionToZero(Result, UnOp->getSubExpr(),
true);
210 return replacementExpression(Result,
false, UnOp->getSubExpr());
214 if (needsNullPtrComparison(
E))
215 return compareExpressionToNullPtr(Result,
E,
false);
217 if (needsZeroComparison(
E))
218 return compareExpressionToZero(Result,
E,
false);
220 StringRef NegatedOperator;
221 const Expr *LHS =
nullptr;
222 const Expr *RHS =
nullptr;
223 if (
const auto *BinOp = dyn_cast<BinaryOperator>(
E)) {
224 NegatedOperator = negatedOperator(BinOp);
225 LHS = BinOp->getLHS();
226 RHS = BinOp->getRHS();
227 }
else if (
const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(
E)) {
228 if (OpExpr->getNumArgs() == 2) {
229 NegatedOperator = negatedOperator(OpExpr);
230 LHS = OpExpr->getArg(0);
231 RHS = OpExpr->getArg(1);
234 if (!NegatedOperator.empty() && LHS && RHS)
235 return (asBool((getText(Result, *LHS) +
" " + NegatedOperator +
" " +
236 getText(Result, *RHS))
240 StringRef
Text = getText(Result, *
E);
241 if (!NeedsStaticCast && needsParensAfterUnaryNegation(
E))
242 return (
"!(" + Text +
")").str();
244 if (needsNullPtrComparison(
E))
245 return compareExpressionToNullPtr(Result,
E,
false);
247 if (needsZeroComparison(
E))
248 return compareExpressionToZero(Result,
E,
false);
250 return (
"!" + asBool(Text, NeedsStaticCast));
253 if (
const auto *UnOp = dyn_cast<UnaryOperator>(
E)) {
254 if (UnOp->getOpcode() == UO_LNot) {
255 if (needsNullPtrComparison(UnOp->getSubExpr()))
256 return compareExpressionToNullPtr(Result, UnOp->getSubExpr(),
false);
258 if (needsZeroComparison(UnOp->getSubExpr()))
259 return compareExpressionToZero(Result, UnOp->getSubExpr(),
false);
263 if (needsNullPtrComparison(
E))
264 return compareExpressionToNullPtr(Result,
E,
true);
266 if (needsZeroComparison(
E))
267 return compareExpressionToZero(Result,
E,
true);
269 return asBool(getText(Result, *
E), NeedsStaticCast);
272 const CXXBoolLiteralExpr *stmtReturnsBool(
const ReturnStmt *Ret,
bool Negated) {
273 if (
const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) {
274 if (Bool->getValue() == !Negated)
281 const CXXBoolLiteralExpr *stmtReturnsBool(
const IfStmt *IfRet,
bool Negated) {
282 if (IfRet->getElse() !=
nullptr)
285 if (
const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen()))
286 return stmtReturnsBool(Ret, Negated);
288 if (
const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) {
289 if (Compound->size() == 1) {
290 if (
const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back()))
291 return stmtReturnsBool(CompoundRet, Negated);
298 bool containsDiscardedTokens(
const MatchFinder::MatchResult &Result,
299 CharSourceRange CharRange) {
300 std::string ReplacementText =
301 Lexer::getSourceText(CharRange, *Result.SourceManager,
302 Result.Context->getLangOpts())
304 Lexer Lex(CharRange.getBegin(), Result.Context->getLangOpts(),
305 ReplacementText.data(), ReplacementText.data(),
306 ReplacementText.data() + ReplacementText.size());
307 Lex.SetCommentRetentionState(
true);
310 while (!Lex.LexFromRawLexer(Tok)) {
311 if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash))
323 const MatchFinder::MatchResult &Result)
324 : Check(Check), Result(Result) {}
327 Check->reportBinOp(Result, Op);
333 const MatchFinder::MatchResult &Result;
336 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef
Name,
339 ChainedConditionalReturn(Options.get(
"ChainedConditionalReturn", false)),
340 ChainedConditionalAssignment(
341 Options.get(
"ChainedConditionalAssignment", false)) {}
346 E =
E->IgnoreParenImpCasts();
347 if (isa<CXXBoolLiteralExpr>(
E))
349 if (
const auto *BinOp = dyn_cast<BinaryOperator>(
E))
352 if (
const auto *UnaryOp = dyn_cast<UnaryOperator>(
E))
357 void SimplifyBooleanExprCheck::reportBinOp(
358 const MatchFinder::MatchResult &Result,
const BinaryOperator *Op) {
359 const auto *LHS = Op->getLHS()->IgnoreParenImpCasts();
360 const auto *RHS = Op->getRHS()->IgnoreParenImpCasts();
362 const CXXBoolLiteralExpr *Bool;
363 const Expr *Other =
nullptr;
364 if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS)))
366 else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS)))
371 if (Bool->getBeginLoc().isMacroID())
378 bool BoolValue = Bool->getValue();
380 auto replaceWithExpression = [
this, &Result, LHS, RHS, Bool](
381 const Expr *ReplaceWith,
bool Negated) {
382 std::string Replacement =
383 replacementExpression(Result, Negated, ReplaceWith);
384 SourceRange
Range(LHS->getBeginLoc(), RHS->getEndLoc());
385 issueDiag(Result, Bool->getBeginLoc(), SimplifyOperatorDiagnostic,
Range,
389 switch (Op->getOpcode()) {
393 replaceWithExpression(Other,
false);
396 replaceWithExpression(Bool,
false);
402 replaceWithExpression(Bool,
false);
405 replaceWithExpression(Other,
false);
410 replaceWithExpression(Other, !BoolValue);
414 replaceWithExpression(Other, BoolValue);
421 void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder,
423 StringRef BooleanId) {
425 ifStmt(unless(isInTemplateInstantiation()),
426 hasCondition(cxxBoolLiteral(equals(Value)).bind(BooleanId)))
431 void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder,
433 StringRef TernaryId) {
435 conditionalOperator(unless(isInTemplateInstantiation()),
436 hasTrueExpression(cxxBoolLiteral(equals(Value))),
437 hasFalseExpression(cxxBoolLiteral(equals(!Value))))
442 void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder,
443 bool Value, StringRef Id) {
444 if (ChainedConditionalReturn)
445 Finder->addMatcher(ifStmt(unless(isInTemplateInstantiation()),
446 hasThen(returnsBool(Value, ThenLiteralId)),
447 hasElse(returnsBool(!Value)))
451 Finder->addMatcher(ifStmt(unless(isInTemplateInstantiation()),
452 unless(hasParent(ifStmt())),
453 hasThen(returnsBool(Value, ThenLiteralId)),
454 hasElse(returnsBool(!Value)))
459 void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder,
460 bool Value, StringRef Id) {
461 auto VarAssign = declRefExpr(hasDeclaration(decl().bind(IfAssignVarId)));
462 auto VarRef = declRefExpr(hasDeclaration(equalsBoundNode(IfAssignVarId)));
463 auto MemAssign = memberExpr(hasDeclaration(decl().bind(IfAssignVarId)));
464 auto MemRef = memberExpr(hasDeclaration(equalsBoundNode(IfAssignVarId)));
466 binaryOperator(hasOperatorName(
"="), hasLHS(anyOf(VarAssign, MemAssign)),
467 hasLHS(expr().bind(IfAssignVariableId)),
468 hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId)));
469 auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1),
470 hasAnySubstatement(SimpleThen)));
472 binaryOperator(hasOperatorName(
"="), hasLHS(anyOf(VarRef, MemRef)),
473 hasRHS(cxxBoolLiteral(equals(!Value))));
474 auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
475 hasAnySubstatement(SimpleElse)));
476 if (ChainedConditionalAssignment)
477 Finder->addMatcher(ifStmt(unless(isInTemplateInstantiation()),
478 hasThen(Then), hasElse(Else))
482 Finder->addMatcher(ifStmt(unless(isInTemplateInstantiation()),
483 unless(hasParent(ifStmt())), hasThen(Then),
489 void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
494 unless(isInTemplateInstantiation()),
496 ifStmt(hasThen(returnsBool(Value)), unless(hasElse(stmt())))),
497 hasAnySubstatement(returnStmt(has(ignoringParenImpCasts(
498 cxxBoolLiteral(equals(!Value)))))
499 .bind(CompoundReturnId)))
505 Options.
store(Opts,
"ChainedConditionalReturn", ChainedConditionalReturn);
507 ChainedConditionalAssignment);
511 Finder->addMatcher(translationUnitDecl().bind(
"top"),
this);
513 matchBoolCondition(Finder,
true, ConditionThenStmtId);
514 matchBoolCondition(Finder,
false, ConditionElseStmtId);
516 matchTernaryResult(Finder,
true, TernaryId);
517 matchTernaryResult(Finder,
false, TernaryNegatedId);
519 matchIfReturnsBool(Finder,
true, IfReturnsBoolId);
520 matchIfReturnsBool(Finder,
false, IfReturnsNotBoolId);
522 matchIfAssignsBool(Finder,
true, IfAssignBoolId);
523 matchIfAssignsBool(Finder,
false, IfAssignNotBoolId);
525 matchCompoundIfReturnsBool(Finder,
true, CompoundBoolId);
526 matchCompoundIfReturnsBool(Finder,
false, CompoundNotBoolId);
530 if (Result.Nodes.getNodeAs<TranslationUnitDecl>(
"top"))
531 Visitor(
this, Result).TraverseAST(*Result.Context);
532 else if (
const CXXBoolLiteralExpr *TrueConditionRemoved =
533 getBoolLiteral(Result, ConditionThenStmtId))
534 replaceWithThenStatement(Result, TrueConditionRemoved);
535 else if (
const CXXBoolLiteralExpr *FalseConditionRemoved =
536 getBoolLiteral(Result, ConditionElseStmtId))
537 replaceWithElseStatement(Result, FalseConditionRemoved);
538 else if (
const auto *Ternary =
539 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId))
540 replaceWithCondition(Result, Ternary);
541 else if (
const auto *TernaryNegated =
542 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId))
543 replaceWithCondition(Result, TernaryNegated,
true);
544 else if (
const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId))
545 replaceWithReturnCondition(Result, If);
546 else if (
const auto *IfNot =
547 Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId))
548 replaceWithReturnCondition(Result, IfNot,
true);
549 else if (
const auto *IfAssign =
550 Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId))
551 replaceWithAssignment(Result, IfAssign);
552 else if (
const auto *IfAssignNot =
553 Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId))
554 replaceWithAssignment(Result, IfAssignNot,
true);
555 else if (
const auto *Compound =
556 Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId))
557 replaceCompoundReturnWithCondition(Result, Compound);
558 else if (
const auto *Compound =
559 Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId))
560 replaceCompoundReturnWithCondition(Result, Compound,
true);
563 void SimplifyBooleanExprCheck::issueDiag(
564 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation
Loc,
565 StringRef
Description, SourceRange ReplacementRange,
566 StringRef Replacement) {
567 CharSourceRange CharRange =
568 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange),
572 if (!containsDiscardedTokens(Result, CharRange))
573 Diag << FixItHint::CreateReplacement(CharRange, Replacement);
576 void SimplifyBooleanExprCheck::replaceWithThenStatement(
577 const MatchFinder::MatchResult &Result,
578 const CXXBoolLiteralExpr *TrueConditionRemoved) {
579 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
580 issueDiag(Result, TrueConditionRemoved->getBeginLoc(),
581 SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
582 getText(Result, *IfStatement->getThen()));
585 void SimplifyBooleanExprCheck::replaceWithElseStatement(
586 const MatchFinder::MatchResult &Result,
587 const CXXBoolLiteralExpr *FalseConditionRemoved) {
588 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
589 const Stmt *ElseStatement = IfStatement->getElse();
590 issueDiag(Result, FalseConditionRemoved->getBeginLoc(),
591 SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
592 ElseStatement ? getText(Result, *ElseStatement) :
"");
595 void SimplifyBooleanExprCheck::replaceWithCondition(
596 const MatchFinder::MatchResult &Result,
const ConditionalOperator *Ternary,
598 std::string Replacement =
599 replacementExpression(Result, Negated, Ternary->getCond());
600 issueDiag(Result, Ternary->getTrueExpr()->getBeginLoc(),
601 "redundant boolean literal in ternary expression result",
602 Ternary->getSourceRange(), Replacement);
605 void SimplifyBooleanExprCheck::replaceWithReturnCondition(
606 const MatchFinder::MatchResult &Result,
const IfStmt *If,
bool Negated) {
607 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ?
";" :
"";
608 std::string
Condition = replacementExpression(Result, Negated, If->getCond());
609 std::string Replacement = (
"return " +
Condition + Terminator).str();
610 SourceLocation Start =
611 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getBeginLoc();
612 issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic,
613 If->getSourceRange(), Replacement);
616 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
617 const MatchFinder::MatchResult &Result,
const CompoundStmt *Compound,
619 const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId);
627 assert(Compound->size() >= 2);
628 const IfStmt *BeforeIf =
nullptr;
629 CompoundStmt::const_body_iterator Current = Compound->body_begin();
630 CompoundStmt::const_body_iterator After = Compound->body_begin();
631 for (++After; After != Compound->body_end() && *Current != Ret;
632 ++Current, ++After) {
633 if (
const auto *If = dyn_cast<IfStmt>(*Current)) {
634 if (
const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) {
636 if (!ChainedConditionalReturn && BeforeIf)
640 std::string Replacement =
641 "return " + replacementExpression(Result, Negated,
Condition);
643 Result, Lit->getBeginLoc(), SimplifyConditionalReturnDiagnostic,
644 SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement);
656 void SimplifyBooleanExprCheck::replaceWithAssignment(
657 const MatchFinder::MatchResult &Result,
const IfStmt *IfAssign,
659 SourceRange
Range = IfAssign->getSourceRange();
660 StringRef VariableName =
661 getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId));
662 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ?
";" :
"";
664 replacementExpression(Result, Negated, IfAssign->getCond());
665 std::string Replacement =
666 (VariableName +
" = " +
Condition + Terminator).str();
668 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getBeginLoc();
670 "redundant boolean literal in conditional assignment",
Range,