clang-tools 10.0.0

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