clang-tools 8.0.0

SimplifyBooleanExprCheck.cpp
Go to the documentation of this file.
1 //===--- SimplifyBooleanExpr.cpp clang-tidy ---------------------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "SimplifyBooleanExprCheck.h"
11 #include "clang/AST/RecursiveASTVisitor.h"
12 #include "clang/Lex/Lexer.h"
13
14 #include <cassert>
15 #include <string>
16 #include <utility>
17
18 using namespace clang::ast_matchers;
19
20 namespace clang {
21 namespace tidy {
22 namespace readability {
23
24 namespace {
25
26 StringRef getText(const MatchFinder::MatchResult &Result, SourceRange Range) {
27 return Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
28 *Result.SourceManager,
29 Result.Context->getLangOpts());
30 }
31
32 template <typename T>
33 StringRef getText(const MatchFinder::MatchResult &Result, T &Node) {
34 return getText(Result, Node.getSourceRange());
35 }
36
37 const char ConditionThenStmtId[] = "if-bool-yields-then";
38 const char ConditionElseStmtId[] = "if-bool-yields-else";
39 const char TernaryId[] = "ternary-bool-yields-condition";
40 const char TernaryNegatedId[] = "ternary-bool-yields-not-condition";
41 const char IfReturnsBoolId[] = "if-return";
42 const char IfReturnsNotBoolId[] = "if-not-return";
43 const char ThenLiteralId[] = "then-literal";
44 const char IfAssignVariableId[] = "if-assign-lvalue";
45 const char IfAssignLocId[] = "if-assign-loc";
46 const char IfAssignBoolId[] = "if-assign";
47 const char IfAssignNotBoolId[] = "if-assign-not";
48 const char IfAssignObjId[] = "if-assign-obj";
49 const char CompoundReturnId[] = "compound-return";
50 const char CompoundBoolId[] = "compound-bool";
51 const char CompoundNotBoolId[] = "compound-bool-not";
52
53 const char IfStmtId[] = "if";
54
55 const char SimplifyOperatorDiagnostic[] =
56 "redundant boolean literal supplied to boolean operator";
57 const char SimplifyConditionDiagnostic[] =
58 "redundant boolean literal in if statement condition";
59 const char SimplifyConditionalReturnDiagnostic[] =
60 "redundant boolean literal in conditional return statement";
61
62 const CXXBoolLiteralExpr *getBoolLiteral(const MatchFinder::MatchResult &Result,
63 StringRef Id) {
64 const auto *Literal = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(Id);
65 return (Literal && Literal->getBeginLoc().isMacroID()) ? nullptr : Literal;
66 }
67
68 internal::Matcher<Stmt> returnsBool(bool Value, StringRef Id = "ignored") {
69 auto SimpleReturnsBool =
70 returnStmt(has(cxxBoolLiteral(equals(Value)).bind(Id)))
71 .bind("returns-bool");
72 return anyOf(SimpleReturnsBool,
73 compoundStmt(statementCountIs(1), has(SimpleReturnsBool)));
74 }
75
76 bool needsParensAfterUnaryNegation(const Expr *E) {
77 E = E->IgnoreImpCasts();
78 if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E))
79 return true;
80
81 if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E))
82 return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
83 Op->getOperator() != OO_Subscript;
84
85 return false;
86 }
87
88 std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = {
89 {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
90
91 StringRef negatedOperator(const BinaryOperator *BinOp) {
92 const BinaryOperatorKind Opcode = BinOp->getOpcode();
93 for (auto NegatableOp : Opposites) {
94 if (Opcode == NegatableOp.first)
95 return BinOp->getOpcodeStr(NegatableOp.second);
96 if (Opcode == NegatableOp.second)
97 return BinOp->getOpcodeStr(NegatableOp.first);
98 }
99 return StringRef();
100 }
101
102 std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = {
103 {OO_EqualEqual, "=="}, {OO_ExclaimEqual, "!="}, {OO_Less, "<"},
104 {OO_GreaterEqual, ">="}, {OO_Greater, ">"}, {OO_LessEqual, "<="}};
105
106 StringRef getOperatorName(OverloadedOperatorKind OpKind) {
107 for (auto Name : OperatorNames) {
108 if (Name.first == OpKind)
109 return Name.second;
110 }
111
112 return StringRef();
113 }
114
115 std::pair<OverloadedOperatorKind, OverloadedOperatorKind> OppositeOverloads[] =
116 {{OO_EqualEqual, OO_ExclaimEqual},
117 {OO_Less, OO_GreaterEqual},
118 {OO_Greater, OO_LessEqual}};
119
120 StringRef negatedOperator(const CXXOperatorCallExpr *OpCall) {
121 const OverloadedOperatorKind Opcode = OpCall->getOperator();
122 for (auto NegatableOp : OppositeOverloads) {
123 if (Opcode == NegatableOp.first)
124 return getOperatorName(NegatableOp.second);
125 if (Opcode == NegatableOp.second)
126 return getOperatorName(NegatableOp.first);
127 }
128 return StringRef();
129 }
130
131 std::string asBool(StringRef text, bool NeedsStaticCast) {
132 if (NeedsStaticCast)
133 return ("static_cast<bool>(" + text + ")").str();
134
135 return text;
136 }
137
138 bool needsNullPtrComparison(const Expr *E) {
139 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
140 return ImpCast->getCastKind() == CK_PointerToBoolean ||
141 ImpCast->getCastKind() == CK_MemberPointerToBoolean;
142
143 return false;
144 }
145
146 bool needsZeroComparison(const Expr *E) {
147 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
148 return ImpCast->getCastKind() == CK_IntegralToBoolean;
149
150 return false;
151 }
152
153 bool needsStaticCast(const Expr *E) {
154 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {
155 if (ImpCast->getCastKind() == CK_UserDefinedConversion &&
156 ImpCast->getSubExpr()->getType()->isBooleanType()) {
157 if (const auto *MemCall =
158 dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) {
159 if (const auto *MemDecl =
160 dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) {
161 if (MemDecl->isExplicit())
162 return true;
163 }
164 }
165 }
166 }
167
168 E = E->IgnoreImpCasts();
169 return !E->getType()->isBooleanType();
170 }
171
172 std::string compareExpressionToConstant(const MatchFinder::MatchResult &Result,
173 const Expr *E, bool Negated,
174 const char *Constant) {
175 E = E->IgnoreImpCasts();
176 const std::string ExprText =
177 (isa<BinaryOperator>(E) ? ("(" + getText(Result, *E) + ")")
178 : getText(Result, *E))
179 .str();
180 return ExprText + " " + (Negated ? "!=" : "==") + " " + Constant;
181 }
182
183 std::string compareExpressionToNullPtr(const MatchFinder::MatchResult &Result,
184 const Expr *E, bool Negated) {
185 const char *NullPtr =
186 Result.Context->getLangOpts().CPlusPlus11 ? "nullptr" : "NULL";
187 return compareExpressionToConstant(Result, E, Negated, NullPtr);
188 }
189
190 std::string compareExpressionToZero(const MatchFinder::MatchResult &Result,
191 const Expr *E, bool Negated) {
192 return compareExpressionToConstant(Result, E, Negated, "0");
193 }
194
195 std::string replacementExpression(const MatchFinder::MatchResult &Result,
196 bool Negated, const Expr *E) {
197 E = E->ignoreParenBaseCasts();
198 if (const auto *EC = dyn_cast<ExprWithCleanups>(E))
199 E = EC->getSubExpr();
200
201 const bool NeedsStaticCast = needsStaticCast(E);
202 if (Negated) {
203 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
204 if (UnOp->getOpcode() == UO_LNot) {
205 if (needsNullPtrComparison(UnOp->getSubExpr()))
206 return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), true);
207
208 if (needsZeroComparison(UnOp->getSubExpr()))
209 return compareExpressionToZero(Result, UnOp->getSubExpr(), true);
210
211 return replacementExpression(Result, false, UnOp->getSubExpr());
212 }
213 }
214
215 if (needsNullPtrComparison(E))
216 return compareExpressionToNullPtr(Result, E, false);
217
218 if (needsZeroComparison(E))
219 return compareExpressionToZero(Result, E, false);
220
221 StringRef NegatedOperator;
222 const Expr *LHS = nullptr;
223 const Expr *RHS = nullptr;
224 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
225 NegatedOperator = negatedOperator(BinOp);
226 LHS = BinOp->getLHS();
227 RHS = BinOp->getRHS();
228 } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
229 if (OpExpr->getNumArgs() == 2) {
230 NegatedOperator = negatedOperator(OpExpr);
231 LHS = OpExpr->getArg(0);
232 RHS = OpExpr->getArg(1);
233 }
234 }
235 if (!NegatedOperator.empty() && LHS && RHS)
236 return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " +
237 getText(Result, *RHS))
238 .str(),
239 NeedsStaticCast));
240
241 StringRef Text = getText(Result, *E);
242 if (!NeedsStaticCast && needsParensAfterUnaryNegation(E))
243 return ("!(" + Text + ")").str();
244
245 if (needsNullPtrComparison(E))
246 return compareExpressionToNullPtr(Result, E, false);
247
248 if (needsZeroComparison(E))
249 return compareExpressionToZero(Result, E, false);
250
251 return ("!" + asBool(Text, NeedsStaticCast));
252 }
253
254 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
255 if (UnOp->getOpcode() == UO_LNot) {
256 if (needsNullPtrComparison(UnOp->getSubExpr()))
257 return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), false);
258
259 if (needsZeroComparison(UnOp->getSubExpr()))
260 return compareExpressionToZero(Result, UnOp->getSubExpr(), false);
261 }
262 }
263
264 if (needsNullPtrComparison(E))
265 return compareExpressionToNullPtr(Result, E, true);
266
267 if (needsZeroComparison(E))
268 return compareExpressionToZero(Result, E, true);
269
270 return asBool(getText(Result, *E), NeedsStaticCast);
271 }
272
273 const CXXBoolLiteralExpr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) {
274 if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) {
275 if (Bool->getValue() == !Negated)
276 return Bool;
277 }
278
279 return nullptr;
280 }
281
282 const CXXBoolLiteralExpr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) {
283 if (IfRet->getElse() != nullptr)
284 return nullptr;
285
286 if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen()))
287 return stmtReturnsBool(Ret, Negated);
288
289 if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) {
290 if (Compound->size() == 1) {
291 if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back()))
292 return stmtReturnsBool(CompoundRet, Negated);
293 }
294 }
295
296 return nullptr;
297 }
298
299 bool containsDiscardedTokens(const MatchFinder::MatchResult &Result,
300 CharSourceRange CharRange) {
301 std::string ReplacementText =
302 Lexer::getSourceText(CharRange, *Result.SourceManager,
303 Result.Context->getLangOpts())
304 .str();
305 Lexer Lex(CharRange.getBegin(), Result.Context->getLangOpts(),
306 ReplacementText.data(), ReplacementText.data(),
307 ReplacementText.data() + ReplacementText.size());
308 Lex.SetCommentRetentionState(true);
309
310 Token Tok;
311 while (!Lex.LexFromRawLexer(Tok)) {
312 if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash))
313 return true;
314 }
315
316 return false;
317 }
318
319 } // namespace
320
321 class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
322 public:
323 Visitor(SimplifyBooleanExprCheck *Check,
324 const MatchFinder::MatchResult &Result)
325 : Check(Check), Result(Result) {}
326
327 bool VisitBinaryOperator(BinaryOperator *Op) {
328 Check->reportBinOp(Result, Op);
329 return true;
330 }
331
332 private:
333 SimplifyBooleanExprCheck *Check;
334 const MatchFinder::MatchResult &Result;
335 };
336
337
338 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
339 ClangTidyContext *Context)
340 : ClangTidyCheck(Name, Context),
341 ChainedConditionalReturn(Options.get("ChainedConditionalReturn", 0U)),
342 ChainedConditionalAssignment(
343 Options.get("ChainedConditionalAssignment", 0U)) {}
344
345 bool containsBoolLiteral(const Expr *E) {
346 if (!E)
347 return false;
348 E = E->IgnoreParenImpCasts();
349 if (isa<CXXBoolLiteralExpr>(E))
350 return true;
351 if (const auto *BinOp = dyn_cast<BinaryOperator>(E))
352 return containsBoolLiteral(BinOp->getLHS()) ||
353 containsBoolLiteral(BinOp->getRHS());
354 if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E))
355 return containsBoolLiteral(UnaryOp->getSubExpr());
356 return false;
357 }
358
359 void SimplifyBooleanExprCheck::reportBinOp(
360 const MatchFinder::MatchResult &Result, const BinaryOperator *Op) {
361 const auto *LHS = Op->getLHS()->IgnoreParenImpCasts();
362 const auto *RHS = Op->getRHS()->IgnoreParenImpCasts();
363
364 const CXXBoolLiteralExpr *Bool = nullptr;
365 const Expr *Other = nullptr;
366 if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS)))
367 Other = RHS;
368 else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS)))
369 Other = LHS;
370 else
371 return;
372
373 if (Bool->getBeginLoc().isMacroID())
374 return;
375
376 // FIXME: why do we need this?
377 if (!isa<CXXBoolLiteralExpr>(Other) && containsBoolLiteral(Other))
378 return;
379
380 bool BoolValue = Bool->getValue();
381
382 auto replaceWithExpression = [this, &Result, LHS, RHS, Bool](
383 const Expr *ReplaceWith, bool Negated) {
384 std::string Replacement =
385 replacementExpression(Result, Negated, ReplaceWith);
386 SourceRange Range(LHS->getBeginLoc(), RHS->getEndLoc());
387 issueDiag(Result, Bool->getBeginLoc(), SimplifyOperatorDiagnostic, Range,
388 Replacement);
389 };
390
391 switch (Op->getOpcode()) {
392 case BO_LAnd:
393 if (BoolValue) {
394 // expr && true -> expr
395 replaceWithExpression(Other, /*Negated=*/false);
396 } else {
397 // expr && false -> false
398 replaceWithExpression(Bool, /*Negated=*/false);
399 }
400 break;
401 case BO_LOr:
402 if (BoolValue) {
403 // expr || true -> true
404 replaceWithExpression(Bool, /*Negated=*/false);
405 } else {
406 // expr || false -> expr
407 replaceWithExpression(Other, /*Negated=*/false);
408 }
409 break;
410 case BO_EQ:
411 // expr == true -> expr, expr == false -> !expr
412 replaceWithExpression(Other, /*Negated=*/!BoolValue);
413 break;
414 case BO_NE:
415 // expr != true -> !expr, expr != false -> expr
416 replaceWithExpression(Other, /*Negated=*/BoolValue);
417 break;
418 default:
419 break;
420 }
421 }
422
423 void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder,
424 bool Value,
425 StringRef BooleanId) {
426 Finder->addMatcher(
427 ifStmt(isExpansionInMainFile(),
428 hasCondition(cxxBoolLiteral(equals(Value)).bind(BooleanId)))
429 .bind(IfStmtId),
430 this);
431 }
432
433 void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder,
434 bool Value,
435 StringRef TernaryId) {
436 Finder->addMatcher(
437 conditionalOperator(isExpansionInMainFile(),
438 hasTrueExpression(cxxBoolLiteral(equals(Value))),
439 hasFalseExpression(cxxBoolLiteral(equals(!Value))))
440 .bind(TernaryId),
441 this);
442 }
443
444 void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder,
445 bool Value, StringRef Id) {
446 if (ChainedConditionalReturn)
447 Finder->addMatcher(ifStmt(isExpansionInMainFile(),
448 hasThen(returnsBool(Value, ThenLiteralId)),
449 hasElse(returnsBool(!Value)))
450 .bind(Id),
451 this);
452 else
453 Finder->addMatcher(ifStmt(isExpansionInMainFile(),
454 unless(hasParent(ifStmt())),
455 hasThen(returnsBool(Value, ThenLiteralId)),
456 hasElse(returnsBool(!Value)))
457 .bind(Id),
458 this);
459 }
460
461 void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder,
462 bool Value, StringRef Id) {
463 auto SimpleThen = binaryOperator(
464 hasOperatorName("="),
465 hasLHS(declRefExpr(hasDeclaration(decl().bind(IfAssignObjId)))),
466 hasLHS(expr().bind(IfAssignVariableId)),
467 hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId)));
468 auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1),
469 hasAnySubstatement(SimpleThen)));
470 auto SimpleElse = binaryOperator(
471 hasOperatorName("="),
472 hasLHS(declRefExpr(hasDeclaration(equalsBoundNode(IfAssignObjId)))),
473 hasRHS(cxxBoolLiteral(equals(!Value))));
474 auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
475 hasAnySubstatement(SimpleElse)));
476 if (ChainedConditionalAssignment)
477 Finder->addMatcher(
478 ifStmt(isExpansionInMainFile(), hasThen(Then), hasElse(Else)).bind(Id),
479 this);
480 else
481 Finder->addMatcher(ifStmt(isExpansionInMainFile(),
482 unless(hasParent(ifStmt())), hasThen(Then),
483 hasElse(Else))
484 .bind(Id),
485 this);
486 }
487
488 void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
489 bool Value,
490 StringRef Id) {
491 Finder->addMatcher(
492 compoundStmt(
493 hasAnySubstatement(
494 ifStmt(hasThen(returnsBool(Value)), unless(hasElse(stmt())))),
495 hasAnySubstatement(returnStmt(has(ignoringParenImpCasts(
496 cxxBoolLiteral(equals(!Value)))))
497 .bind(CompoundReturnId)))
498 .bind(Id),
499 this);
500 }
501
502 void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
503 Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
504 Options.store(Opts, "ChainedConditionalAssignment",
505 ChainedConditionalAssignment);
506 }
507
508 void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) {
509 Finder->addMatcher(translationUnitDecl().bind("top"), this);
510
511 matchBoolCondition(Finder, true, ConditionThenStmtId);
512 matchBoolCondition(Finder, false, ConditionElseStmtId);
513
514 matchTernaryResult(Finder, true, TernaryId);
515 matchTernaryResult(Finder, false, TernaryNegatedId);
516
517 matchIfReturnsBool(Finder, true, IfReturnsBoolId);
518 matchIfReturnsBool(Finder, false, IfReturnsNotBoolId);
519
520 matchIfAssignsBool(Finder, true, IfAssignBoolId);
521 matchIfAssignsBool(Finder, false, IfAssignNotBoolId);
522
523 matchCompoundIfReturnsBool(Finder, true, CompoundBoolId);
524 matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId);
525 }
526
527 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
528 if (Result.Nodes.getNodeAs<TranslationUnitDecl>("top"))
529 Visitor(this, Result).TraverseAST(*Result.Context);
530 else if (const CXXBoolLiteralExpr *TrueConditionRemoved =
531 getBoolLiteral(Result, ConditionThenStmtId))
532 replaceWithThenStatement(Result, TrueConditionRemoved);
533 else if (const CXXBoolLiteralExpr *FalseConditionRemoved =
534 getBoolLiteral(Result, ConditionElseStmtId))
535 replaceWithElseStatement(Result, FalseConditionRemoved);
536 else if (const auto *Ternary =
537 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId))
538 replaceWithCondition(Result, Ternary);
539 else if (const auto *TernaryNegated =
540 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId))
541 replaceWithCondition(Result, TernaryNegated, true);
542 else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId))
543 replaceWithReturnCondition(Result, If);
544 else if (const auto *IfNot =
545 Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId))
546 replaceWithReturnCondition(Result, IfNot, true);
547 else if (const auto *IfAssign =
548 Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId))
549 replaceWithAssignment(Result, IfAssign);
550 else if (const auto *IfAssignNot =
551 Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId))
552 replaceWithAssignment(Result, IfAssignNot, true);
553 else if (const auto *Compound =
554 Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId))
555 replaceCompoundReturnWithCondition(Result, Compound);
556 else if (const auto *Compound =
557 Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId))
558 replaceCompoundReturnWithCondition(Result, Compound, true);
559 }
560
561 void SimplifyBooleanExprCheck::issueDiag(
562 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc,
563 StringRef Description, SourceRange ReplacementRange,
564 StringRef Replacement) {
565 CharSourceRange CharRange =
566 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange),
567 *Result.SourceManager, getLangOpts());
568
569 DiagnosticBuilder Diag = diag(Loc, Description);
570 if (!containsDiscardedTokens(Result, CharRange))
571 Diag << FixItHint::CreateReplacement(CharRange, Replacement);
572 }
573
574 void SimplifyBooleanExprCheck::replaceWithThenStatement(
575 const MatchFinder::MatchResult &Result,
576 const CXXBoolLiteralExpr *TrueConditionRemoved) {
577 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
578 issueDiag(Result, TrueConditionRemoved->getBeginLoc(),
579 SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
580 getText(Result, *IfStatement->getThen()));
581 }
582
583 void SimplifyBooleanExprCheck::replaceWithElseStatement(
584 const MatchFinder::MatchResult &Result,
585 const CXXBoolLiteralExpr *FalseConditionRemoved) {
586 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
587 const Stmt *ElseStatement = IfStatement->getElse();
588 issueDiag(Result, FalseConditionRemoved->getBeginLoc(),
589 SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
590 ElseStatement ? getText(Result, *ElseStatement) : "");
591 }
592
593 void SimplifyBooleanExprCheck::replaceWithCondition(
594 const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary,
595 bool Negated) {
596 std::string Replacement =
597 replacementExpression(Result, Negated, Ternary->getCond());
598 issueDiag(Result, Ternary->getTrueExpr()->getBeginLoc(),
599 "redundant boolean literal in ternary expression result",
600 Ternary->getSourceRange(), Replacement);
601 }
602
603 void SimplifyBooleanExprCheck::replaceWithReturnCondition(
604 const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) {
605 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : "";
606 std::string Condition = replacementExpression(Result, Negated, If->getCond());
607 std::string Replacement = ("return " + Condition + Terminator).str();
608 SourceLocation Start =
609 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getBeginLoc();
610 issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic,
611 If->getSourceRange(), Replacement);
612 }
613
614 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
615 const MatchFinder::MatchResult &Result, const CompoundStmt *Compound,
616 bool Negated) {
617 const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId);
618
619 // The body shouldn't be empty because the matcher ensures that it must
620 // contain at least two statements:
621 // 1) A `return` statement returning a boolean literal `false` or `true`
622 // 2) An `if` statement with no `else` clause that consists of a single
623 // `return` statement returning the opposite boolean literal `true` or
624 // `false`.
625 assert(Compound->size() >= 2);
626 const IfStmt *BeforeIf = nullptr;
627 CompoundStmt::const_body_iterator Current = Compound->body_begin();
628 CompoundStmt::const_body_iterator After = Compound->body_begin();
629 for (++After; After != Compound->body_end() && *Current != Ret;
630 ++Current, ++After) {
631 if (const auto *If = dyn_cast<IfStmt>(*Current)) {
632 if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) {
633 if (*After == Ret) {
634 if (!ChainedConditionalReturn && BeforeIf)
635 continue;
636
637 const Expr *Condition = If->getCond();
638 std::string Replacement =
639 "return " + replacementExpression(Result, Negated, Condition);
640 issueDiag(
641 Result, Lit->getBeginLoc(), SimplifyConditionalReturnDiagnostic,
642 SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement);
643 return;
644 }
645
646 BeforeIf = If;
647 }
648 } else {
649 BeforeIf = nullptr;
650 }
651 }
652 }
653
654 void SimplifyBooleanExprCheck::replaceWithAssignment(
655 const MatchFinder::MatchResult &Result, const IfStmt *IfAssign,
656 bool Negated) {
657 SourceRange Range = IfAssign->getSourceRange();
658 StringRef VariableName =
659 getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId));
660 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : "";
661 std::string Condition =
662 replacementExpression(Result, Negated, IfAssign->getCond());
663 std::string Replacement =
664 (VariableName + " = " + Condition + Terminator).str();
665 SourceLocation Location =
666 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getBeginLoc();
667 issueDiag(Result, Location,
668 "redundant boolean literal in conditional assignment", Range,
669 Replacement);
670 }
671
672 } // namespace readability
673 } // namespace tidy
674 } // namespace clang
Loc
SourceLocation Loc
&#39;#&#39; location in the include directive
Definition: IncludeOrderCheck.cpp:37
clang::ast_matchers
Definition: AbseilMatcher.h:15
clang::tidy::OptionsView::store
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.
Definition: ClangTidy.cpp:473
clang::tidy::readability::containsBoolLiteral
bool containsBoolLiteral(const Expr *E)
Definition: SimplifyBooleanExprCheck.cpp:345
clang::tidy::ClangTidyCheck::getLangOpts
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
clang::tidy::readability::SimplifyBooleanExprCheck::Visitor::Visitor
Visitor(SimplifyBooleanExprCheck *Check, const MatchFinder::MatchResult &Result)
Definition: SimplifyBooleanExprCheck.cpp:323
SimplifyBooleanExprCheck.h
clang::tidy::readability::SimplifyBooleanExprCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Options) override
Should store all options supported by this check with their current values or default values for opti...
Definition: SimplifyBooleanExprCheck.cpp:502
clang::tidy::readability::SimplifyBooleanExprCheck::Visitor::VisitBinaryOperator
bool VisitBinaryOperator(BinaryOperator *Op)
Definition: SimplifyBooleanExprCheck.cpp:327
clang::tidy::readability::SimplifyBooleanExprCheck
Looks for boolean expressions involving boolean constants and simplifies them to use the appropriate ...
Definition: SimplifyBooleanExprCheck.h:24
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidy.h:183
clang::clangd::CompletionItemKind::Text
clang::tidy::readability::SimplifyBooleanExprCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: SimplifyBooleanExprCheck.cpp:527
clang::tidy::readability::SimplifyBooleanExprCheck::Visitor
Definition: SimplifyBooleanExprCheck.cpp:321
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:28
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:98
Result
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: ClangdServer.cpp:60
clang::tidy::readability::SimplifyBooleanExprCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: SimplifyBooleanExprCheck.cpp:508
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ChangeNamespace.cpp:18
Condition
std::string Condition
Condition used after the preprocessor directive.
Definition: RedundantPreprocessorCheck.cpp:22
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:100
Description
const char * Description
Definition: Dexp.cpp:258
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:438
Location
Definition: Modularize.cpp:384