blob: 704a49e8b40cff81989ff4ed1c880f900c287465 [file] [log] [blame]
Alexander Kornienkof5e72b02015-04-10 19:26:43 +00001//===--- 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>
Alexander Kornienko6ae400d2015-07-01 12:39:40 +000015#include <utility>
Alexander Kornienkof5e72b02015-04-10 19:26:43 +000016
17using namespace clang::ast_matchers;
18
19namespace clang {
20namespace tidy {
21namespace readability {
22
23namespace {
24
25StringRef getText(const MatchFinder::MatchResult &Result, SourceRange Range) {
26 return Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
27 *Result.SourceManager,
28 Result.Context->getLangOpts());
29}
30
31template <typename T>
32StringRef getText(const MatchFinder::MatchResult &Result, T &Node) {
33 return getText(Result, Node.getSourceRange());
34}
35
36const char RightExpressionId[] = "bool-op-expr-yields-expr";
37const char LeftExpressionId[] = "expr-op-bool-yields-expr";
38const char NegatedRightExpressionId[] = "bool-op-expr-yields-not-expr";
39const char NegatedLeftExpressionId[] = "expr-op-bool-yields-not-expr";
40const char ConditionThenStmtId[] = "if-bool-yields-then";
41const char ConditionElseStmtId[] = "if-bool-yields-else";
42const char TernaryId[] = "ternary-bool-yields-condition";
43const char TernaryNegatedId[] = "ternary-bool-yields-not-condition";
44const char IfReturnsBoolId[] = "if-return";
45const char IfReturnsNotBoolId[] = "if-not-return";
46const char ThenLiteralId[] = "then-literal";
47const char IfAssignVariableId[] = "if-assign-lvalue";
48const char IfAssignLocId[] = "if-assign-loc";
49const char IfAssignBoolId[] = "if-assign";
50const char IfAssignNotBoolId[] = "if-assign-not";
51const char IfAssignObjId[] = "if-assign-obj";
Alexander Kornienko6ae400d2015-07-01 12:39:40 +000052const char CompoundReturnId[] = "compound-return";
53const char CompoundBoolId[] = "compound-bool";
54const char CompoundNotBoolId[] = "compound-bool-not";
Alexander Kornienkof5e72b02015-04-10 19:26:43 +000055
56const char IfStmtId[] = "if";
57const char LHSId[] = "lhs-expr";
58const char RHSId[] = "rhs-expr";
59
60const char SimplifyOperatorDiagnostic[] =
61 "redundant boolean literal supplied to boolean operator";
62const char SimplifyConditionDiagnostic[] =
63 "redundant boolean literal in if statement condition";
Alexander Kornienko6ae400d2015-07-01 12:39:40 +000064const char SimplifyConditionalReturnDiagnostic[] =
65 "redundant boolean literal in conditional return statement";
Alexander Kornienkof5e72b02015-04-10 19:26:43 +000066
67const 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
Alexander Kornienko6ae400d2015-07-01 12:39:40 +000076internal::Matcher<Stmt> returnsBool(bool Value, StringRef Id = "ignored") {
77 auto SimpleReturnsBool =
78 returnStmt(has(boolLiteral(equals(Value)).bind(Id))).bind("returns-bool");
Alexander Kornienkof5e72b02015-04-10 19:26:43 +000079 return anyOf(SimpleReturnsBool,
80 compoundStmt(statementCountIs(1), has(SimpleReturnsBool)));
81}
82
83bool needsParensAfterUnaryNegation(const Expr *E) {
Alexander Kornienko6ae400d2015-07-01 12:39:40 +000084 E = E->IgnoreImpCasts();
Alexander Kornienkof5e72b02015-04-10 19:26:43 +000085 if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E))
86 return true;
Alexander Kornienko6ae400d2015-07-01 12:39:40 +000087 if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E)) {
Alexander Kornienkof5e72b02015-04-10 19:26:43 +000088 return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
89 Op->getOperator() != OO_Subscript;
Alexander Kornienko6ae400d2015-07-01 12:39:40 +000090 }
Alexander Kornienkof5e72b02015-04-10 19:26:43 +000091 return false;
92}
93
94std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = {
Alexander Kornienko6ae400d2015-07-01 12:39:40 +000095 {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
Alexander Kornienkof5e72b02015-04-10 19:26:43 +000096
97StringRef negatedOperator(const BinaryOperator *BinOp) {
98 const BinaryOperatorKind Opcode = BinOp->getOpcode();
99 for (auto NegatableOp : Opposites) {
100 if (Opcode == NegatableOp.first)
101 return BinOp->getOpcodeStr(NegatableOp.second);
102 if (Opcode == NegatableOp.second)
103 return BinOp->getOpcodeStr(NegatableOp.first);
104 }
105 return StringRef();
106}
107
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000108std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = {
109 {OO_EqualEqual, "=="}, {OO_ExclaimEqual, "!="}, {OO_Less, "<"},
110 {OO_GreaterEqual, ">="}, {OO_Greater, ">"}, {OO_LessEqual, "<="}};
111
112StringRef getOperatorName(OverloadedOperatorKind OpKind) {
113 for (auto Name : OperatorNames) {
114 if (Name.first == OpKind)
115 return Name.second;
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000116 }
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000117
118 return StringRef();
119}
120
121std::pair<OverloadedOperatorKind, OverloadedOperatorKind> OppositeOverloads[] =
122 {{OO_EqualEqual, OO_ExclaimEqual},
123 {OO_Less, OO_GreaterEqual},
124 {OO_Greater, OO_LessEqual}};
125
126StringRef negatedOperator(const CXXOperatorCallExpr *OpCall) {
127 const OverloadedOperatorKind Opcode = OpCall->getOperator();
128 for (auto NegatableOp : OppositeOverloads) {
129 if (Opcode == NegatableOp.first)
130 return getOperatorName(NegatableOp.second);
131 if (Opcode == NegatableOp.second)
132 return getOperatorName(NegatableOp.first);
133 }
134 return StringRef();
135}
136
137std::string asBool(StringRef text, bool NeedsStaticCast) {
138 if (NeedsStaticCast)
139 return ("static_cast<bool>(" + text + ")").str();
140
141 return text;
142}
143
144bool needsNullPtrComparison(const Expr *E) {
145 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
146 return ImpCast->getCastKind() == CK_PointerToBoolean;
147
148 return false;
149}
150
151bool needsStaticCast(const Expr *E) {
152 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {
153 if (ImpCast->getCastKind() == CK_UserDefinedConversion &&
154 ImpCast->getSubExpr()->getType()->isBooleanType()) {
155 if (const auto *MemCall =
156 dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) {
157 if (const auto *MemDecl =
158 dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) {
159 if (MemDecl->isExplicit())
160 return true;
161 }
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000162 }
163 }
164 }
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000165
166 E = E->IgnoreImpCasts();
167 return !E->getType()->isBooleanType();
168}
169
170std::string replacementExpression(const MatchFinder::MatchResult &Result,
171 bool Negated, const Expr *E) {
172 E = E->ignoreParenBaseCasts();
173 const bool NeedsStaticCast = needsStaticCast(E);
174 if (Negated) {
175 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
176 if (UnOp->getOpcode() == UO_LNot) {
177 if (needsNullPtrComparison(UnOp->getSubExpr()))
178 return (getText(Result, *UnOp->getSubExpr()) + " != nullptr").str();
179
180 return replacementExpression(Result, false, UnOp->getSubExpr());
181 }
182 }
183
184 if (needsNullPtrComparison(E))
185 return (getText(Result, *E) + " == nullptr").str();
186
187 StringRef NegatedOperator;
188 const Expr *LHS = nullptr;
189 const Expr *RHS = nullptr;
190 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
191 NegatedOperator = negatedOperator(BinOp);
192 LHS = BinOp->getLHS();
193 RHS = BinOp->getRHS();
194 } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
195 if (OpExpr->getNumArgs() == 2) {
196 NegatedOperator = negatedOperator(OpExpr);
197 LHS = OpExpr->getArg(0);
198 RHS = OpExpr->getArg(1);
199 }
200 }
201 if (!NegatedOperator.empty() && LHS && RHS) {
202 return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " +
203 getText(Result, *RHS))
204 .str(),
205 NeedsStaticCast));
206 }
207
208 StringRef Text = getText(Result, *E);
209 if (!NeedsStaticCast && needsParensAfterUnaryNegation(E))
210 return ("!(" + Text + ")").str();
211
212 if (needsNullPtrComparison(E))
213 return (getText(Result, *E) + " == nullptr").str();
214
215 return ("!" + asBool(Text, NeedsStaticCast));
216 }
217
218 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
219 if (UnOp->getOpcode() == UO_LNot) {
220 if (needsNullPtrComparison(UnOp->getSubExpr()))
221 return (getText(Result, *UnOp->getSubExpr()) + " == nullptr").str();
222 }
223 }
224
225 if (needsNullPtrComparison(E))
226 return (getText(Result, *E) + " != nullptr").str();
227
228 return asBool(getText(Result, *E), NeedsStaticCast);
229}
230
231const CXXBoolLiteralExpr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) {
232 if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) {
233 if (Bool->getValue() == !Negated)
234 return Bool;
235 }
236
237 return nullptr;
238}
239
240const CXXBoolLiteralExpr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) {
241 if (IfRet->getElse() != nullptr)
242 return nullptr;
243
244 if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen()))
245 return stmtReturnsBool(Ret, Negated);
246
247 if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) {
248 if (Compound->size() == 1) {
249 if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back()))
250 return stmtReturnsBool(CompoundRet, Negated);
251 }
252 }
253
254 return nullptr;
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000255}
256
257} // namespace
258
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000259SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
260 ClangTidyContext *Context)
261 : ClangTidyCheck(Name, Context),
262 ChainedConditionalReturn(Options.get("ChainedConditionalReturn", 0U)),
263 ChainedConditionalAssignment(
264 Options.get("ChainedConditionalAssignment", 0U)) {}
265
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000266void SimplifyBooleanExprCheck::matchBoolBinOpExpr(MatchFinder *Finder,
267 bool Value,
268 StringRef OperatorName,
269 StringRef BooleanId) {
270 Finder->addMatcher(
271 binaryOperator(isExpansionInMainFile(), hasOperatorName(OperatorName),
272 hasLHS(allOf(expr().bind(LHSId),
273 boolLiteral(equals(Value)).bind(BooleanId))),
274 hasRHS(expr().bind(RHSId)),
275 unless(hasRHS(hasDescendant(boolLiteral())))),
276 this);
277}
278
279void SimplifyBooleanExprCheck::matchExprBinOpBool(MatchFinder *Finder,
280 bool Value,
281 StringRef OperatorName,
282 StringRef BooleanId) {
283 Finder->addMatcher(
284 binaryOperator(
285 isExpansionInMainFile(), hasOperatorName(OperatorName),
286 hasLHS(expr().bind(LHSId)),
287 unless(hasLHS(anyOf(boolLiteral(), hasDescendant(boolLiteral())))),
288 hasRHS(allOf(expr().bind(RHSId),
289 boolLiteral(equals(Value)).bind(BooleanId)))),
290 this);
291}
292
293void SimplifyBooleanExprCheck::matchBoolCompOpExpr(MatchFinder *Finder,
294 bool Value,
295 StringRef OperatorName,
296 StringRef BooleanId) {
297 Finder->addMatcher(
298 binaryOperator(isExpansionInMainFile(), hasOperatorName(OperatorName),
299 hasLHS(allOf(expr().bind(LHSId),
300 ignoringImpCasts(boolLiteral(equals(Value))
301 .bind(BooleanId)))),
302 hasRHS(expr().bind(RHSId)),
303 unless(hasRHS(hasDescendant(boolLiteral())))),
304 this);
305}
306
307void SimplifyBooleanExprCheck::matchExprCompOpBool(MatchFinder *Finder,
308 bool Value,
309 StringRef OperatorName,
310 StringRef BooleanId) {
311 Finder->addMatcher(
312 binaryOperator(isExpansionInMainFile(), hasOperatorName(OperatorName),
313 unless(hasLHS(hasDescendant(boolLiteral()))),
314 hasLHS(expr().bind(LHSId)),
315 hasRHS(allOf(expr().bind(RHSId),
316 ignoringImpCasts(boolLiteral(equals(Value))
317 .bind(BooleanId))))),
318 this);
319}
320
321void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder,
322 bool Value,
323 StringRef BooleanId) {
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000324 Finder->addMatcher(
325 ifStmt(isExpansionInMainFile(),
326 hasCondition(boolLiteral(equals(Value)).bind(BooleanId)))
327 .bind(IfStmtId),
328 this);
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000329}
330
331void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder,
332 bool Value,
333 StringRef TernaryId) {
334 Finder->addMatcher(
335 conditionalOperator(isExpansionInMainFile(),
336 hasTrueExpression(boolLiteral(equals(Value))),
337 hasFalseExpression(boolLiteral(equals(!Value))))
338 .bind(TernaryId),
339 this);
340}
341
342void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder,
343 bool Value, StringRef Id) {
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000344 if (ChainedConditionalReturn) {
345 Finder->addMatcher(ifStmt(isExpansionInMainFile(),
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000346 hasThen(returnsBool(Value, ThenLiteralId)),
347 hasElse(returnsBool(!Value)))
348 .bind(Id),
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000349 this);
350 } else {
351 Finder->addMatcher(ifStmt(isExpansionInMainFile(),
352 unless(hasParent(ifStmt())),
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000353 hasThen(returnsBool(Value, ThenLiteralId)),
354 hasElse(returnsBool(!Value)))
355 .bind(Id),
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000356 this);
357 }
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000358}
359
360void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder,
361 bool Value, StringRef Id) {
362 auto SimpleThen = binaryOperator(
363 hasOperatorName("="),
364 hasLHS(declRefExpr(hasDeclaration(decl().bind(IfAssignObjId)))),
365 hasLHS(expr().bind(IfAssignVariableId)),
366 hasRHS(boolLiteral(equals(Value)).bind(IfAssignLocId)));
367 auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1),
368 hasAnySubstatement(SimpleThen)));
369 auto SimpleElse = binaryOperator(
370 hasOperatorName("="),
371 hasLHS(declRefExpr(hasDeclaration(equalsBoundNode(IfAssignObjId)))),
372 hasRHS(boolLiteral(equals(!Value))));
373 auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
374 hasAnySubstatement(SimpleElse)));
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000375 if (ChainedConditionalAssignment) {
376 Finder->addMatcher(
377 ifStmt(isExpansionInMainFile(), hasThen(Then), hasElse(Else)).bind(Id),
378 this);
379 } else {
380 Finder->addMatcher(ifStmt(isExpansionInMainFile(),
381 unless(hasParent(ifStmt())), hasThen(Then),
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000382 hasElse(Else))
383 .bind(Id),
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000384 this);
385 }
386}
387
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000388void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
389 bool Value,
390 StringRef Id) {
391 Finder->addMatcher(
392 compoundStmt(
393 allOf(hasAnySubstatement(ifStmt(hasThen(returnsBool(Value)),
394 unless(hasElse(stmt())))),
395 hasAnySubstatement(returnStmt(has(boolLiteral(equals(!Value))))
396 .bind(CompoundReturnId))))
397 .bind(Id),
398 this);
399}
400
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000401void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
402 Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
403 Options.store(Opts, "ChainedConditionalAssignment",
404 ChainedConditionalAssignment);
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000405}
406
407void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) {
408 matchBoolBinOpExpr(Finder, true, "&&", RightExpressionId);
409 matchBoolBinOpExpr(Finder, false, "||", RightExpressionId);
410 matchExprBinOpBool(Finder, false, "&&", RightExpressionId);
411 matchExprBinOpBool(Finder, true, "||", RightExpressionId);
412 matchBoolCompOpExpr(Finder, true, "==", RightExpressionId);
413 matchBoolCompOpExpr(Finder, false, "!=", RightExpressionId);
414
415 matchExprBinOpBool(Finder, true, "&&", LeftExpressionId);
416 matchExprBinOpBool(Finder, false, "||", LeftExpressionId);
417 matchBoolBinOpExpr(Finder, false, "&&", LeftExpressionId);
418 matchBoolBinOpExpr(Finder, true, "||", LeftExpressionId);
419 matchExprCompOpBool(Finder, true, "==", LeftExpressionId);
420 matchExprCompOpBool(Finder, false, "!=", LeftExpressionId);
421
422 matchBoolCompOpExpr(Finder, false, "==", NegatedRightExpressionId);
423 matchBoolCompOpExpr(Finder, true, "!=", NegatedRightExpressionId);
424
425 matchExprCompOpBool(Finder, false, "==", NegatedLeftExpressionId);
426 matchExprCompOpBool(Finder, true, "!=", NegatedLeftExpressionId);
427
428 matchBoolCondition(Finder, true, ConditionThenStmtId);
429 matchBoolCondition(Finder, false, ConditionElseStmtId);
430
431 matchTernaryResult(Finder, true, TernaryId);
432 matchTernaryResult(Finder, false, TernaryNegatedId);
433
434 matchIfReturnsBool(Finder, true, IfReturnsBoolId);
435 matchIfReturnsBool(Finder, false, IfReturnsNotBoolId);
436
437 matchIfAssignsBool(Finder, true, IfAssignBoolId);
438 matchIfAssignsBool(Finder, false, IfAssignNotBoolId);
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000439
440 matchCompoundIfReturnsBool(Finder, true, CompoundBoolId);
441 matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId);
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000442}
443
444void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
445 if (const CXXBoolLiteralExpr *LeftRemoved =
446 getBoolLiteral(Result, RightExpressionId)) {
447 replaceWithExpression(Result, LeftRemoved, false);
448 } else if (const CXXBoolLiteralExpr *RightRemoved =
449 getBoolLiteral(Result, LeftExpressionId)) {
450 replaceWithExpression(Result, RightRemoved, true);
451 } else if (const CXXBoolLiteralExpr *NegatedLeftRemoved =
452 getBoolLiteral(Result, NegatedRightExpressionId)) {
453 replaceWithExpression(Result, NegatedLeftRemoved, false, true);
454 } else if (const CXXBoolLiteralExpr *NegatedRightRemoved =
455 getBoolLiteral(Result, NegatedLeftExpressionId)) {
456 replaceWithExpression(Result, NegatedRightRemoved, true, true);
457 } else if (const CXXBoolLiteralExpr *TrueConditionRemoved =
458 getBoolLiteral(Result, ConditionThenStmtId)) {
459 replaceWithThenStatement(Result, TrueConditionRemoved);
460 } else if (const CXXBoolLiteralExpr *FalseConditionRemoved =
461 getBoolLiteral(Result, ConditionElseStmtId)) {
462 replaceWithElseStatement(Result, FalseConditionRemoved);
463 } else if (const auto *Ternary =
464 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId)) {
465 replaceWithCondition(Result, Ternary);
466 } else if (const auto *TernaryNegated =
467 Result.Nodes.getNodeAs<ConditionalOperator>(
468 TernaryNegatedId)) {
469 replaceWithCondition(Result, TernaryNegated, true);
470 } else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId)) {
471 replaceWithReturnCondition(Result, If);
472 } else if (const auto *IfNot =
473 Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId)) {
474 replaceWithReturnCondition(Result, IfNot, true);
475 } else if (const auto *IfAssign =
476 Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId)) {
477 replaceWithAssignment(Result, IfAssign);
478 } else if (const auto *IfAssignNot =
479 Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId)) {
480 replaceWithAssignment(Result, IfAssignNot, true);
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000481 } else if (const auto *Compound =
482 Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId)) {
483 replaceCompoundReturnWithCondition(Result, Compound);
484 } else if (const auto *Compound =
485 Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId)) {
486 replaceCompoundReturnWithCondition(Result, Compound, true);
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000487 }
488}
489
490void SimplifyBooleanExprCheck::replaceWithExpression(
491 const ast_matchers::MatchFinder::MatchResult &Result,
492 const CXXBoolLiteralExpr *BoolLiteral, bool UseLHS, bool Negated) {
493 const auto *LHS = Result.Nodes.getNodeAs<Expr>(LHSId);
494 const auto *RHS = Result.Nodes.getNodeAs<Expr>(RHSId);
495 std::string Replacement =
496 replacementExpression(Result, Negated, UseLHS ? LHS : RHS);
497 SourceLocation Start = LHS->getLocStart();
498 SourceLocation End = RHS->getLocEnd();
499 diag(BoolLiteral->getLocStart(), SimplifyOperatorDiagnostic)
500 << FixItHint::CreateReplacement(SourceRange(Start, End), Replacement);
501}
502
503void SimplifyBooleanExprCheck::replaceWithThenStatement(
504 const MatchFinder::MatchResult &Result,
505 const CXXBoolLiteralExpr *TrueConditionRemoved) {
506 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
507 diag(TrueConditionRemoved->getLocStart(), SimplifyConditionDiagnostic)
508 << FixItHint::CreateReplacement(IfStatement->getSourceRange(),
509 getText(Result, *IfStatement->getThen()));
510}
511
512void SimplifyBooleanExprCheck::replaceWithElseStatement(
513 const MatchFinder::MatchResult &Result,
514 const CXXBoolLiteralExpr *FalseConditionRemoved) {
515 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
516 const Stmt *ElseStatement = IfStatement->getElse();
517 diag(FalseConditionRemoved->getLocStart(), SimplifyConditionDiagnostic)
518 << FixItHint::CreateReplacement(
519 IfStatement->getSourceRange(),
520 ElseStatement ? getText(Result, *ElseStatement) : "");
521}
522
523void SimplifyBooleanExprCheck::replaceWithCondition(
524 const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary,
525 bool Negated) {
526 std::string Replacement =
527 replacementExpression(Result, Negated, Ternary->getCond());
528 diag(Ternary->getTrueExpr()->getLocStart(),
529 "redundant boolean literal in ternary expression result")
530 << FixItHint::CreateReplacement(Ternary->getSourceRange(), Replacement);
531}
532
533void SimplifyBooleanExprCheck::replaceWithReturnCondition(
534 const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) {
535 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : "";
536 std::string Condition = replacementExpression(Result, Negated, If->getCond());
537 std::string Replacement = ("return " + Condition + Terminator).str();
538 SourceLocation Start =
539 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getLocStart();
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000540 diag(Start, SimplifyConditionalReturnDiagnostic)
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000541 << FixItHint::CreateReplacement(If->getSourceRange(), Replacement);
542}
543
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000544void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
545 const MatchFinder::MatchResult &Result, const CompoundStmt *Compound,
546 bool Negated) {
547 const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId);
548
549 // The body shouldn't be empty because the matcher ensures that it must
550 // contain at least two statements:
551 // 1) A `return` statement returning a boolean literal `false` or `true`
552 // 2) An `if` statement with no `else` clause that consists fo a single
553 // `return` statement returning the opposite boolean literal `true` or
554 // `false`.
555 assert(Compound->size() >= 2);
556 const IfStmt *BeforeIf = nullptr;
557 CompoundStmt::const_body_iterator Current = Compound->body_begin();
558 CompoundStmt::const_body_iterator After = Compound->body_begin();
559 for (++After; After != Compound->body_end() && *Current != Ret;
560 ++Current, ++After) {
561 if (const auto *If = dyn_cast<IfStmt>(*Current)) {
562 if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) {
563 if (*After == Ret) {
564 if (!ChainedConditionalReturn && BeforeIf)
565 continue;
566
567 const Expr *Condition = If->getCond();
568 std::string Replacement =
569 "return " + replacementExpression(Result, Negated, Condition);
570 diag(Lit->getLocStart(), SimplifyConditionalReturnDiagnostic)
571 << FixItHint::CreateReplacement(
572 SourceRange(If->getLocStart(), Ret->getLocEnd()),
573 Replacement);
574 return;
575 }
576
577 BeforeIf = If;
578 }
579 } else {
580 BeforeIf = nullptr;
581 }
582 }
583}
584
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000585void SimplifyBooleanExprCheck::replaceWithAssignment(
586 const MatchFinder::MatchResult &Result, const IfStmt *IfAssign,
587 bool Negated) {
588 SourceRange Range = IfAssign->getSourceRange();
589 StringRef VariableName =
590 getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId));
591 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : "";
592 std::string Condition =
593 replacementExpression(Result, Negated, IfAssign->getCond());
594 std::string Replacement =
595 (VariableName + " = " + Condition + Terminator).str();
596 SourceLocation Location =
597 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getLocStart();
598 this->diag(Location, "redundant boolean literal in conditional assignment")
599 << FixItHint::CreateReplacement(Range, Replacement);
600}
601
602} // namespace readability
603} // namespace tidy
604} // namespace clang