clang-tools 3.8.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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/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 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";
55
56 const char IfStmtId[] = "if";
57 const char LHSId[] = "lhs-expr";
58 const char RHSId[] = "rhs-expr";
59
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";
66
67 const CXXBoolLiteralExpr *getBoolLiteral(const MatchFinder::MatchResult &Result,
68 StringRef Id) {
69 const auto *Literal = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(Id);
70 return (Literal &&
71 Result.SourceManager->isMacroBodyExpansion(Literal->getLocStart()))
72 ? nullptr
73 : Literal;
74 }
75
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)));
82 }
83
84 bool needsParensAfterUnaryNegation(const Expr *E) {
85 E = E->IgnoreImpCasts();
86 if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E))
87 return true;
88 if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E)) {
89 return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
90 Op->getOperator() != OO_Subscript;
91 }
92 return false;
93 }
94
95 std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = {
96 {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
97
98 StringRef negatedOperator(const BinaryOperator *BinOp) {
99 const BinaryOperatorKind Opcode = BinOp->getOpcode();
100 for (auto NegatableOp : Opposites) {
101 if (Opcode == NegatableOp.first)
102 return BinOp->getOpcodeStr(NegatableOp.second);
103 if (Opcode == NegatableOp.second)
104 return BinOp->getOpcodeStr(NegatableOp.first);
105 }
106 return StringRef();
107 }
108
109 std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = {
110 {OO_EqualEqual, "=="},
111 {OO_ExclaimEqual, "!="},
112 {OO_Less, "<"},
113 {OO_GreaterEqual, ">="},
114 {OO_Greater, ">"},
115 {OO_LessEqual, "<="}};
116
117 StringRef getOperatorName(OverloadedOperatorKind OpKind) {
118 for (auto Name : OperatorNames) {
119 if (Name.first == OpKind)
120 return Name.second;
121 }
122
123 return StringRef();
124 }
125
126 std::pair<OverloadedOperatorKind, OverloadedOperatorKind> OppositeOverloads[] =
127 {{OO_EqualEqual, OO_ExclaimEqual},
128 {OO_Less, OO_GreaterEqual},
129 {OO_Greater, OO_LessEqual}};
130
131 StringRef negatedOperator(const CXXOperatorCallExpr *OpCall) {
132 const OverloadedOperatorKind Opcode = OpCall->getOperator();
133 for (auto NegatableOp : OppositeOverloads) {
134 if (Opcode == NegatableOp.first)
135 return getOperatorName(NegatableOp.second);
136 if (Opcode == NegatableOp.second)
137 return getOperatorName(NegatableOp.first);
138 }
139 return StringRef();
140 }
141
142 std::string asBool(StringRef text, bool NeedsStaticCast) {
143 if (NeedsStaticCast)
144 return ("static_cast<bool>(" + text + ")").str();
145
146 return text;
147 }
148
149 bool needsNullPtrComparison(const Expr *E) {
150 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
151 return ImpCast->getCastKind() == CK_PointerToBoolean;
152
153 return false;
154 }
155
156 bool needsStaticCast(const Expr *E) {
157 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {
158 if (ImpCast->getCastKind() == CK_UserDefinedConversion &&
159 ImpCast->getSubExpr()->getType()->isBooleanType()) {
160 if (const auto *MemCall =
161 dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) {
162 if (const auto *MemDecl =
163 dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) {
164 if (MemDecl->isExplicit())
165 return true;
166 }
167 }
168 }
169 }
170
171 E = E->IgnoreImpCasts();
172 return !E->getType()->isBooleanType();
173 }
174
175 std::string replacementExpression(const MatchFinder::MatchResult &Result,
176 bool Negated, const Expr *E) {
177 E = E->ignoreParenBaseCasts();
178 const bool NeedsStaticCast = needsStaticCast(E);
179 if (Negated) {
180 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
181 if (UnOp->getOpcode() == UO_LNot) {
182 if (needsNullPtrComparison(UnOp->getSubExpr()))
183 return (getText(Result, *UnOp->getSubExpr()) + " != nullptr").str();
184
185 return replacementExpression(Result, false, UnOp->getSubExpr());
186 }
187 }
188
189 if (needsNullPtrComparison(E))
190 return (getText(Result, *E) + " == nullptr").str();
191
192 StringRef NegatedOperator;
193 const Expr *LHS = nullptr;
194 const Expr *RHS = nullptr;
195 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
196 NegatedOperator = negatedOperator(BinOp);
197 LHS = BinOp->getLHS();
198 RHS = BinOp->getRHS();
199 } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
200 if (OpExpr->getNumArgs() == 2) {
201 NegatedOperator = negatedOperator(OpExpr);
202 LHS = OpExpr->getArg(0);
203 RHS = OpExpr->getArg(1);
204 }
205 }
206 if (!NegatedOperator.empty() && LHS && RHS) {
207 return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " +
208 getText(Result, *RHS)).str(),
209 NeedsStaticCast));
210 }
211
212 StringRef Text = getText(Result, *E);
213 if (!NeedsStaticCast && needsParensAfterUnaryNegation(E))
214 return ("!(" + Text + ")").str();
215
216 if (needsNullPtrComparison(E))
217 return (getText(Result, *E) + " == nullptr").str();
218
219 return ("!" + asBool(Text, NeedsStaticCast));
220 }
221
222 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
223 if (UnOp->getOpcode() == UO_LNot) {
224 if (needsNullPtrComparison(UnOp->getSubExpr()))
225 return (getText(Result, *UnOp->getSubExpr()) + " == nullptr").str();
226 }
227 }
228
229 if (needsNullPtrComparison(E))
230 return (getText(Result, *E) + " != nullptr").str();
231
232 return asBool(getText(Result, *E), NeedsStaticCast);
233 }
234
235 const CXXBoolLiteralExpr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) {
236 if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) {
237 if (Bool->getValue() == !Negated)
238 return Bool;
239 }
240
241 return nullptr;
242 }
243
244 const CXXBoolLiteralExpr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) {
245 if (IfRet->getElse() != nullptr)
246 return nullptr;
247
248 if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen()))
249 return stmtReturnsBool(Ret, Negated);
250
251 if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) {
252 if (Compound->size() == 1) {
253 if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back()))
254 return stmtReturnsBool(CompoundRet, Negated);
255 }
256 }
257
258 return nullptr;
259 }
260
261 } // namespace
262
263 SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
264 ClangTidyContext *Context)
265 : ClangTidyCheck(Name, Context),
266 ChainedConditionalReturn(Options.get("ChainedConditionalReturn", 0U)),
267 ChainedConditionalAssignment(
268 Options.get("ChainedConditionalAssignment", 0U)) {}
269
270 void SimplifyBooleanExprCheck::matchBoolBinOpExpr(MatchFinder *Finder,
271 bool Value,
272 StringRef OperatorName,
273 StringRef BooleanId) {
274 Finder->addMatcher(
275 binaryOperator(
276 isExpansionInMainFile(), hasOperatorName(OperatorName),
277 hasLHS(allOf(expr().bind(LHSId),
278 cxxBoolLiteral(equals(Value)).bind(BooleanId))),
279 hasRHS(expr().bind(RHSId)),
280 unless(hasRHS(hasDescendant(cxxBoolLiteral())))),
281 this);
282 }
283
284 void SimplifyBooleanExprCheck::matchExprBinOpBool(MatchFinder *Finder,
285 bool Value,
286 StringRef OperatorName,
287 StringRef BooleanId) {
288 Finder->addMatcher(
289 binaryOperator(
290 isExpansionInMainFile(), hasOperatorName(OperatorName),
291 hasLHS(expr().bind(LHSId)),
292 unless(
293 hasLHS(anyOf(cxxBoolLiteral(), hasDescendant(cxxBoolLiteral())))),
294 hasRHS(allOf(expr().bind(RHSId),
295 cxxBoolLiteral(equals(Value)).bind(BooleanId)))),
296 this);
297 }
298
299 void SimplifyBooleanExprCheck::matchBoolCompOpExpr(MatchFinder *Finder,
300 bool Value,
301 StringRef OperatorName,
302 StringRef BooleanId) {
303 Finder->addMatcher(
304 binaryOperator(isExpansionInMainFile(), hasOperatorName(OperatorName),
305 hasLHS(allOf(expr().bind(LHSId),
306 ignoringImpCasts(cxxBoolLiteral(equals(Value))
307 .bind(BooleanId)))),
308 hasRHS(expr().bind(RHSId)),
309 unless(hasRHS(hasDescendant(cxxBoolLiteral())))),
310 this);
311 }
312
313 void SimplifyBooleanExprCheck::matchExprCompOpBool(MatchFinder *Finder,
314 bool Value,
315 StringRef OperatorName,
316 StringRef BooleanId) {
317 Finder->addMatcher(
318 binaryOperator(isExpansionInMainFile(), hasOperatorName(OperatorName),
319 unless(hasLHS(hasDescendant(cxxBoolLiteral()))),
320 hasLHS(expr().bind(LHSId)),
321 hasRHS(allOf(expr().bind(RHSId),
322 ignoringImpCasts(cxxBoolLiteral(equals(Value))
323 .bind(BooleanId))))),
324 this);
325 }
326
327 void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder,
328 bool Value,
329 StringRef BooleanId) {
330 Finder->addMatcher(ifStmt(isExpansionInMainFile(),
331 hasCondition(cxxBoolLiteral(equals(Value))
332 .bind(BooleanId))).bind(IfStmtId),
333 this);
334 }
335
336 void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder,
337 bool Value,
338 StringRef TernaryId) {
339 Finder->addMatcher(
340 conditionalOperator(isExpansionInMainFile(),
341 hasTrueExpression(cxxBoolLiteral(equals(Value))),
342 hasFalseExpression(cxxBoolLiteral(equals(!Value))))
343 .bind(TernaryId),
344 this);
345 }
346
347 void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder,
348 bool Value, StringRef Id) {
349 if (ChainedConditionalReturn) {
350 Finder->addMatcher(ifStmt(isExpansionInMainFile(),
351 hasThen(returnsBool(Value, ThenLiteralId)),
352 hasElse(returnsBool(!Value))).bind(Id),
353 this);
354 } else {
355 Finder->addMatcher(ifStmt(isExpansionInMainFile(),
356 unless(hasParent(ifStmt())),
357 hasThen(returnsBool(Value, ThenLiteralId)),
358 hasElse(returnsBool(!Value))).bind(Id),
359 this);
360 }
361 }
362
363 void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder,
364 bool Value, StringRef Id) {
365 auto SimpleThen = binaryOperator(
366 hasOperatorName("="),
367 hasLHS(declRefExpr(hasDeclaration(decl().bind(IfAssignObjId)))),
368 hasLHS(expr().bind(IfAssignVariableId)),
369 hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId)));
370 auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1),
371 hasAnySubstatement(SimpleThen)));
372 auto SimpleElse = binaryOperator(
373 hasOperatorName("="),
374 hasLHS(declRefExpr(hasDeclaration(equalsBoundNode(IfAssignObjId)))),
375 hasRHS(cxxBoolLiteral(equals(!Value))));
376 auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
377 hasAnySubstatement(SimpleElse)));
378 if (ChainedConditionalAssignment) {
379 Finder->addMatcher(
380 ifStmt(isExpansionInMainFile(), hasThen(Then), hasElse(Else)).bind(Id),
381 this);
382 } else {
383 Finder->addMatcher(ifStmt(isExpansionInMainFile(),
384 unless(hasParent(ifStmt())), hasThen(Then),
385 hasElse(Else)).bind(Id),
386 this);
387 }
388 }
389
390 void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
391 bool Value,
392 StringRef Id) {
393 Finder->addMatcher(
394 compoundStmt(allOf(hasAnySubstatement(ifStmt(hasThen(returnsBool(Value)),
395 unless(hasElse(stmt())))),
396 hasAnySubstatement(
397 returnStmt(has(cxxBoolLiteral(equals(!Value))))
398 .bind(CompoundReturnId)))).bind(Id),
399 this);
400 }
401
402 void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
403 Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
404 Options.store(Opts, "ChainedConditionalAssignment",
405 ChainedConditionalAssignment);
406 }
407
408 void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) {
409 matchBoolBinOpExpr(Finder, true, "&&", RightExpressionId);
410 matchBoolBinOpExpr(Finder, false, "||", RightExpressionId);
411 matchExprBinOpBool(Finder, false, "&&", RightExpressionId);
412 matchExprBinOpBool(Finder, true, "||", RightExpressionId);
413 matchBoolCompOpExpr(Finder, true, "==", RightExpressionId);
414 matchBoolCompOpExpr(Finder, false, "!=", RightExpressionId);
415
416 matchExprBinOpBool(Finder, true, "&&", LeftExpressionId);
417 matchExprBinOpBool(Finder, false, "||", LeftExpressionId);
418 matchBoolBinOpExpr(Finder, false, "&&", LeftExpressionId);
419 matchBoolBinOpExpr(Finder, true, "||", LeftExpressionId);
420 matchExprCompOpBool(Finder, true, "==", LeftExpressionId);
421 matchExprCompOpBool(Finder, false, "!=", LeftExpressionId);
422
423 matchBoolCompOpExpr(Finder, false, "==", NegatedRightExpressionId);
424 matchBoolCompOpExpr(Finder, true, "!=", NegatedRightExpressionId);
425
426 matchExprCompOpBool(Finder, false, "==", NegatedLeftExpressionId);
427 matchExprCompOpBool(Finder, true, "!=", NegatedLeftExpressionId);
428
429 matchBoolCondition(Finder, true, ConditionThenStmtId);
430 matchBoolCondition(Finder, false, ConditionElseStmtId);
431
432 matchTernaryResult(Finder, true, TernaryId);
433 matchTernaryResult(Finder, false, TernaryNegatedId);
434
435 matchIfReturnsBool(Finder, true, IfReturnsBoolId);
436 matchIfReturnsBool(Finder, false, IfReturnsNotBoolId);
437
438 matchIfAssignsBool(Finder, true, IfAssignBoolId);
439 matchIfAssignsBool(Finder, false, IfAssignNotBoolId);
440
441 matchCompoundIfReturnsBool(Finder, true, CompoundBoolId);
442 matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId);
443 }
444
445 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
446 if (const CXXBoolLiteralExpr *LeftRemoved =
447 getBoolLiteral(Result, RightExpressionId)) {
448 replaceWithExpression(Result, LeftRemoved, false);
449 } else if (const CXXBoolLiteralExpr *RightRemoved =
450 getBoolLiteral(Result, LeftExpressionId)) {
451 replaceWithExpression(Result, RightRemoved, true);
452 } else if (const CXXBoolLiteralExpr *NegatedLeftRemoved =
453 getBoolLiteral(Result, NegatedRightExpressionId)) {
454 replaceWithExpression(Result, NegatedLeftRemoved, false, true);
455 } else if (const CXXBoolLiteralExpr *NegatedRightRemoved =
456 getBoolLiteral(Result, NegatedLeftExpressionId)) {
457 replaceWithExpression(Result, NegatedRightRemoved, true, true);
458 } else if (const CXXBoolLiteralExpr *TrueConditionRemoved =
459 getBoolLiteral(Result, ConditionThenStmtId)) {
460 replaceWithThenStatement(Result, TrueConditionRemoved);
461 } else if (const CXXBoolLiteralExpr *FalseConditionRemoved =
462 getBoolLiteral(Result, ConditionElseStmtId)) {
463 replaceWithElseStatement(Result, FalseConditionRemoved);
464 } else if (const auto *Ternary =
465 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId)) {
466 replaceWithCondition(Result, Ternary);
467 } else if (const auto *TernaryNegated =
468 Result.Nodes.getNodeAs<ConditionalOperator>(
469 TernaryNegatedId)) {
470 replaceWithCondition(Result, TernaryNegated, true);
471 } else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId)) {
472 replaceWithReturnCondition(Result, If);
473 } else if (const auto *IfNot =
474 Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId)) {
475 replaceWithReturnCondition(Result, IfNot, true);
476 } else if (const auto *IfAssign =
477 Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId)) {
478 replaceWithAssignment(Result, IfAssign);
479 } else if (const auto *IfAssignNot =
480 Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId)) {
481 replaceWithAssignment(Result, IfAssignNot, true);
482 } else if (const auto *Compound =
483 Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId)) {
484 replaceCompoundReturnWithCondition(Result, Compound);
485 } else if (const auto *Compound =
486 Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId)) {
487 replaceCompoundReturnWithCondition(Result, Compound, true);
488 }
489 }
490
491 bool containsDiscardedTokens(
492 const ast_matchers::MatchFinder::MatchResult &Result,
493 CharSourceRange CharRange) {
494 std::string ReplacementText =
495 Lexer::getSourceText(CharRange, *Result.SourceManager,
496 Result.Context->getLangOpts())
497 .str();
498 Lexer Lex(CharRange.getBegin(), Result.Context->getLangOpts(),
499 ReplacementText.data(), ReplacementText.data(),
500 ReplacementText.data() + ReplacementText.size());
501 Lex.SetCommentRetentionState(true);
502 Token Tok;
503
504 while (!Lex.LexFromRawLexer(Tok)) {
505 if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash))
506 return true;
507 }
508
509 return false;
510 }
511
512 void SimplifyBooleanExprCheck::issueDiag(
513 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc,
514 StringRef Description, SourceRange ReplacementRange,
515 StringRef Replacement) {
516 CharSourceRange CharRange = Lexer::makeFileCharRange(
517 CharSourceRange::getTokenRange(ReplacementRange), *Result.SourceManager,
518 Result.Context->getLangOpts());
519
520 DiagnosticBuilder Diag = diag(Loc, Description);
521 if (!containsDiscardedTokens(Result, CharRange))
522 Diag << FixItHint::CreateReplacement(CharRange, Replacement);
523 }
524
525 void SimplifyBooleanExprCheck::replaceWithExpression(
526 const ast_matchers::MatchFinder::MatchResult &Result,
527 const CXXBoolLiteralExpr *BoolLiteral, bool UseLHS, bool Negated) {
528 const auto *LHS = Result.Nodes.getNodeAs<Expr>(LHSId);
529 const auto *RHS = Result.Nodes.getNodeAs<Expr>(RHSId);
530 std::string Replacement =
531 replacementExpression(Result, Negated, UseLHS ? LHS : RHS);
532 SourceRange Range(LHS->getLocStart(), RHS->getLocEnd());
533 issueDiag(Result, BoolLiteral->getLocStart(), SimplifyOperatorDiagnostic,
534 Range, Replacement);
535 }
536
537 void SimplifyBooleanExprCheck::replaceWithThenStatement(
538 const MatchFinder::MatchResult &Result,
539 const CXXBoolLiteralExpr *TrueConditionRemoved) {
540 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
541 issueDiag(Result, TrueConditionRemoved->getLocStart(),
542 SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
543 getText(Result, *IfStatement->getThen()));
544 }
545
546 void SimplifyBooleanExprCheck::replaceWithElseStatement(
547 const MatchFinder::MatchResult &Result,
548 const CXXBoolLiteralExpr *FalseConditionRemoved) {
549 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
550 const Stmt *ElseStatement = IfStatement->getElse();
551 issueDiag(Result, FalseConditionRemoved->getLocStart(),
552 SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
553 ElseStatement ? getText(Result, *ElseStatement) : "");
554 }
555
556 void SimplifyBooleanExprCheck::replaceWithCondition(
557 const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary,
558 bool Negated) {
559 std::string Replacement =
560 replacementExpression(Result, Negated, Ternary->getCond());
561 issueDiag(Result, Ternary->getTrueExpr()->getLocStart(),
562 "redundant boolean literal in ternary expression result",
563 Ternary->getSourceRange(), Replacement);
564 }
565
566 void SimplifyBooleanExprCheck::replaceWithReturnCondition(
567 const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) {
568 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : "";
569 std::string Condition = replacementExpression(Result, Negated, If->getCond());
570 std::string Replacement = ("return " + Condition + Terminator).str();
571 SourceLocation Start =
572 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getLocStart();
573 issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic,
574 If->getSourceRange(), Replacement);
575 }
576
577 void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
578 const MatchFinder::MatchResult &Result, const CompoundStmt *Compound,
579 bool Negated) {
580 const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId);
581
582 // The body shouldn't be empty because the matcher ensures that it must
583 // contain at least two statements:
584 // 1) A `return` statement returning a boolean literal `false` or `true`
585 // 2) An `if` statement with no `else` clause that consists fo a single
586 // `return` statement returning the opposite boolean literal `true` or
587 // `false`.
588 assert(Compound->size() >= 2);
589 const IfStmt *BeforeIf = nullptr;
590 CompoundStmt::const_body_iterator Current = Compound->body_begin();
591 CompoundStmt::const_body_iterator After = Compound->body_begin();
592 for (++After; After != Compound->body_end() && *Current != Ret;
593 ++Current, ++After) {
594 if (const auto *If = dyn_cast<IfStmt>(*Current)) {
595 if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) {
596 if (*After == Ret) {
597 if (!ChainedConditionalReturn && BeforeIf)
598 continue;
599
600 const Expr *Condition = If->getCond();
601 std::string Replacement =
602 "return " + replacementExpression(Result, Negated, Condition);
603 issueDiag(
604 Result, Lit->getLocStart(), SimplifyConditionalReturnDiagnostic,
605 SourceRange(If->getLocStart(), Ret->getLocEnd()), Replacement);
606 return;
607 }
608
609 BeforeIf = If;
610 }
611 } else {
612 BeforeIf = nullptr;
613 }
614 }
615 }
616
617 void SimplifyBooleanExprCheck::replaceWithAssignment(
618 const MatchFinder::MatchResult &Result, const IfStmt *IfAssign,
619 bool Negated) {
620 SourceRange Range = IfAssign->getSourceRange();
621 StringRef VariableName =
622 getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId));
623 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : "";
624 std::string Condition =
625 replacementExpression(Result, Negated, IfAssign->getCond());
626 std::string Replacement =
627 (VariableName + " = " + Condition + Terminator).str();
628 SourceLocation Location =
629 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getLocStart();
630 issueDiag(Result, Location,
631 "redundant boolean literal in conditional assignment", Range,
632 Replacement);
633 }
634
635 } // namespace readability
636 } // namespace tidy
637 } // namespace clang
Loc
SourceLocation Loc
'#' location in the include directive
Definition: IncludeOrderCheck.cpp:34
Name
StringHandle Name
Definition: PreprocessorTracker.cpp:525
Finder
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
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:402
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidy.h:102
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidy.h:158
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:445
clang::tidy::readability::containsDiscardedTokens
bool containsDiscardedTokens(const ast_matchers::MatchFinder::MatchResult &Result, CharSourceRange CharRange)
Definition: SimplifyBooleanExprCheck.cpp:491
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:344
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:82
clang::tidy::readability::SimplifyBooleanExprCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register ASTMatchers with Finder.
Definition: SimplifyBooleanExprCheck.cpp:408
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:35
Context
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:124
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidy.cpp:323
Result
const NamedDecl * Result
Definition: USRFinder.cpp:121
Location
Definition: Modularize.cpp:386