blob: be64335af703b9cfbd3500f843541607e7b258db [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 =
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +000078 returnStmt(has(cxxBoolLiteral(equals(Value)).bind(Id)))
79 .bind("returns-bool");
Alexander Kornienkof5e72b02015-04-10 19:26:43 +000080 return anyOf(SimpleReturnsBool,
81 compoundStmt(statementCountIs(1), has(SimpleReturnsBool)));
82}
83
84bool needsParensAfterUnaryNegation(const Expr *E) {
Alexander Kornienko6ae400d2015-07-01 12:39:40 +000085 E = E->IgnoreImpCasts();
Alexander Kornienkof5e72b02015-04-10 19:26:43 +000086 if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E))
87 return true;
Aaron Ballmanf034a8c2016-02-12 15:09:05 +000088
89 if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E))
Alexander Kornienkof5e72b02015-04-10 19:26:43 +000090 return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
91 Op->getOperator() != OO_Subscript;
Aaron Ballmanf034a8c2016-02-12 15:09:05 +000092
Alexander Kornienkof5e72b02015-04-10 19:26:43 +000093 return false;
94}
95
96std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = {
Alexander Kornienko6ae400d2015-07-01 12:39:40 +000097 {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
Alexander Kornienkof5e72b02015-04-10 19:26:43 +000098
99StringRef negatedOperator(const BinaryOperator *BinOp) {
100 const BinaryOperatorKind Opcode = BinOp->getOpcode();
101 for (auto NegatableOp : Opposites) {
102 if (Opcode == NegatableOp.first)
103 return BinOp->getOpcodeStr(NegatableOp.second);
104 if (Opcode == NegatableOp.second)
105 return BinOp->getOpcodeStr(NegatableOp.first);
106 }
107 return StringRef();
108}
109
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000110std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = {
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000111 {OO_EqualEqual, "=="}, {OO_ExclaimEqual, "!="}, {OO_Less, "<"},
112 {OO_GreaterEqual, ">="}, {OO_Greater, ">"}, {OO_LessEqual, "<="}};
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000113
114StringRef getOperatorName(OverloadedOperatorKind OpKind) {
115 for (auto Name : OperatorNames) {
116 if (Name.first == OpKind)
117 return Name.second;
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000118 }
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000119
120 return StringRef();
121}
122
123std::pair<OverloadedOperatorKind, OverloadedOperatorKind> OppositeOverloads[] =
124 {{OO_EqualEqual, OO_ExclaimEqual},
125 {OO_Less, OO_GreaterEqual},
126 {OO_Greater, OO_LessEqual}};
127
128StringRef negatedOperator(const CXXOperatorCallExpr *OpCall) {
129 const OverloadedOperatorKind Opcode = OpCall->getOperator();
130 for (auto NegatableOp : OppositeOverloads) {
131 if (Opcode == NegatableOp.first)
132 return getOperatorName(NegatableOp.second);
133 if (Opcode == NegatableOp.second)
134 return getOperatorName(NegatableOp.first);
135 }
136 return StringRef();
137}
138
139std::string asBool(StringRef text, bool NeedsStaticCast) {
140 if (NeedsStaticCast)
141 return ("static_cast<bool>(" + text + ")").str();
142
143 return text;
144}
145
146bool needsNullPtrComparison(const Expr *E) {
147 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000148 return ImpCast->getCastKind() == CK_PointerToBoolean ||
149 ImpCast->getCastKind() == CK_MemberPointerToBoolean;
150
151 return false;
152}
153
154bool needsZeroComparison(const Expr *E) {
155 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
156 return ImpCast->getCastKind() == CK_IntegralToBoolean;
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000157
158 return false;
159}
160
161bool needsStaticCast(const Expr *E) {
162 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {
163 if (ImpCast->getCastKind() == CK_UserDefinedConversion &&
164 ImpCast->getSubExpr()->getType()->isBooleanType()) {
165 if (const auto *MemCall =
166 dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) {
167 if (const auto *MemDecl =
168 dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) {
169 if (MemDecl->isExplicit())
170 return true;
171 }
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000172 }
173 }
174 }
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000175
176 E = E->IgnoreImpCasts();
177 return !E->getType()->isBooleanType();
178}
179
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000180std::string compareExpressionToConstant(const MatchFinder::MatchResult &Result,
181 const Expr *E, bool Negated,
182 const char *Constant) {
183 E = E->IgnoreImpCasts();
184 const std::string ExprText =
185 (isa<BinaryOperator>(E) ? ("(" + getText(Result, *E) + ")")
186 : getText(Result, *E))
187 .str();
188 return ExprText + " " + (Negated ? "!=" : "==") + " " + Constant;
189}
190
191std::string compareExpressionToNullPtr(const MatchFinder::MatchResult &Result,
192 const Expr *E, bool Negated) {
193 const char *NullPtr =
194 Result.Context->getLangOpts().CPlusPlus11 ? "nullptr" : "NULL";
195 return compareExpressionToConstant(Result, E, Negated, NullPtr);
196}
197
198std::string compareExpressionToZero(const MatchFinder::MatchResult &Result,
199 const Expr *E, bool Negated) {
200 return compareExpressionToConstant(Result, E, Negated, "0");
201}
202
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000203std::string replacementExpression(const MatchFinder::MatchResult &Result,
204 bool Negated, const Expr *E) {
205 E = E->ignoreParenBaseCasts();
206 const bool NeedsStaticCast = needsStaticCast(E);
207 if (Negated) {
208 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
209 if (UnOp->getOpcode() == UO_LNot) {
210 if (needsNullPtrComparison(UnOp->getSubExpr()))
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000211 return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), true);
212
213 if (needsZeroComparison(UnOp->getSubExpr()))
214 return compareExpressionToZero(Result, UnOp->getSubExpr(), true);
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000215
216 return replacementExpression(Result, false, UnOp->getSubExpr());
217 }
218 }
219
220 if (needsNullPtrComparison(E))
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000221 return compareExpressionToNullPtr(Result, E, false);
222
223 if (needsZeroComparison(E))
224 return compareExpressionToZero(Result, E, false);
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000225
226 StringRef NegatedOperator;
227 const Expr *LHS = nullptr;
228 const Expr *RHS = nullptr;
229 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
230 NegatedOperator = negatedOperator(BinOp);
231 LHS = BinOp->getLHS();
232 RHS = BinOp->getRHS();
233 } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
234 if (OpExpr->getNumArgs() == 2) {
235 NegatedOperator = negatedOperator(OpExpr);
236 LHS = OpExpr->getArg(0);
237 RHS = OpExpr->getArg(1);
238 }
239 }
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000240 if (!NegatedOperator.empty() && LHS && RHS)
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000241 return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " +
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000242 getText(Result, *RHS))
243 .str(),
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000244 NeedsStaticCast));
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000245
246 StringRef Text = getText(Result, *E);
247 if (!NeedsStaticCast && needsParensAfterUnaryNegation(E))
248 return ("!(" + Text + ")").str();
249
250 if (needsNullPtrComparison(E))
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000251 return compareExpressionToNullPtr(Result, E, false);
252
253 if (needsZeroComparison(E))
254 return compareExpressionToZero(Result, E, false);
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000255
256 return ("!" + asBool(Text, NeedsStaticCast));
257 }
258
259 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
260 if (UnOp->getOpcode() == UO_LNot) {
261 if (needsNullPtrComparison(UnOp->getSubExpr()))
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000262 return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), false);
263
264 if (needsZeroComparison(UnOp->getSubExpr()))
265 return compareExpressionToZero(Result, UnOp->getSubExpr(), false);
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000266 }
267 }
268
269 if (needsNullPtrComparison(E))
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000270 return compareExpressionToNullPtr(Result, E, true);
271
272 if (needsZeroComparison(E))
273 return compareExpressionToZero(Result, E, true);
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000274
275 return asBool(getText(Result, *E), NeedsStaticCast);
276}
277
278const CXXBoolLiteralExpr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) {
279 if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) {
280 if (Bool->getValue() == !Negated)
281 return Bool;
282 }
283
284 return nullptr;
285}
286
287const CXXBoolLiteralExpr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) {
288 if (IfRet->getElse() != nullptr)
289 return nullptr;
290
291 if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen()))
292 return stmtReturnsBool(Ret, Negated);
293
294 if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) {
295 if (Compound->size() == 1) {
296 if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back()))
297 return stmtReturnsBool(CompoundRet, Negated);
298 }
299 }
300
301 return nullptr;
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000302}
303
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000304bool containsDiscardedTokens(const MatchFinder::MatchResult &Result,
305 CharSourceRange CharRange) {
306 std::string ReplacementText =
307 Lexer::getSourceText(CharRange, *Result.SourceManager,
308 Result.Context->getLangOpts())
309 .str();
310 Lexer Lex(CharRange.getBegin(), Result.Context->getLangOpts(),
311 ReplacementText.data(), ReplacementText.data(),
312 ReplacementText.data() + ReplacementText.size());
313 Lex.SetCommentRetentionState(true);
314
315 Token Tok;
316 while (!Lex.LexFromRawLexer(Tok)) {
317 if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash))
318 return true;
319 }
320
321 return false;
322}
323
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000324} // namespace
325
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000326SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
327 ClangTidyContext *Context)
328 : ClangTidyCheck(Name, Context),
329 ChainedConditionalReturn(Options.get("ChainedConditionalReturn", 0U)),
330 ChainedConditionalAssignment(
331 Options.get("ChainedConditionalAssignment", 0U)) {}
332
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000333void SimplifyBooleanExprCheck::matchBoolBinOpExpr(MatchFinder *Finder,
334 bool Value,
335 StringRef OperatorName,
336 StringRef BooleanId) {
337 Finder->addMatcher(
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000338 binaryOperator(
339 isExpansionInMainFile(), hasOperatorName(OperatorName),
340 hasLHS(allOf(expr().bind(LHSId),
341 cxxBoolLiteral(equals(Value)).bind(BooleanId))),
342 hasRHS(expr().bind(RHSId)),
343 unless(hasRHS(hasDescendant(cxxBoolLiteral())))),
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000344 this);
345}
346
347void SimplifyBooleanExprCheck::matchExprBinOpBool(MatchFinder *Finder,
348 bool Value,
349 StringRef OperatorName,
350 StringRef BooleanId) {
351 Finder->addMatcher(
352 binaryOperator(
353 isExpansionInMainFile(), hasOperatorName(OperatorName),
354 hasLHS(expr().bind(LHSId)),
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000355 unless(
356 hasLHS(anyOf(cxxBoolLiteral(), hasDescendant(cxxBoolLiteral())))),
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000357 hasRHS(allOf(expr().bind(RHSId),
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000358 cxxBoolLiteral(equals(Value)).bind(BooleanId)))),
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000359 this);
360}
361
362void SimplifyBooleanExprCheck::matchBoolCompOpExpr(MatchFinder *Finder,
363 bool Value,
364 StringRef OperatorName,
365 StringRef BooleanId) {
366 Finder->addMatcher(
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000367 binaryOperator(
368 isExpansionInMainFile(), hasOperatorName(OperatorName),
369 hasLHS(allOf(
370 expr().bind(LHSId),
371 ignoringImpCasts(cxxBoolLiteral(equals(Value)).bind(BooleanId)))),
372 hasRHS(expr().bind(RHSId)),
373 unless(hasRHS(hasDescendant(cxxBoolLiteral())))),
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000374 this);
375}
376
377void SimplifyBooleanExprCheck::matchExprCompOpBool(MatchFinder *Finder,
378 bool Value,
379 StringRef OperatorName,
380 StringRef BooleanId) {
381 Finder->addMatcher(
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000382 binaryOperator(
383 isExpansionInMainFile(), hasOperatorName(OperatorName),
384 unless(hasLHS(hasDescendant(cxxBoolLiteral()))),
385 hasLHS(expr().bind(LHSId)),
386 hasRHS(allOf(expr().bind(RHSId),
387 ignoringImpCasts(
388 cxxBoolLiteral(equals(Value)).bind(BooleanId))))),
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000389 this);
390}
391
392void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder,
393 bool Value,
394 StringRef BooleanId) {
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000395 Finder->addMatcher(
396 ifStmt(isExpansionInMainFile(),
397 hasCondition(cxxBoolLiteral(equals(Value)).bind(BooleanId)))
398 .bind(IfStmtId),
399 this);
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000400}
401
402void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder,
403 bool Value,
404 StringRef TernaryId) {
405 Finder->addMatcher(
406 conditionalOperator(isExpansionInMainFile(),
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000407 hasTrueExpression(cxxBoolLiteral(equals(Value))),
408 hasFalseExpression(cxxBoolLiteral(equals(!Value))))
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000409 .bind(TernaryId),
410 this);
411}
412
413void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder,
414 bool Value, StringRef Id) {
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000415 if (ChainedConditionalReturn)
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000416 Finder->addMatcher(ifStmt(isExpansionInMainFile(),
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000417 hasThen(returnsBool(Value, ThenLiteralId)),
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000418 hasElse(returnsBool(!Value)))
419 .bind(Id),
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000420 this);
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000421 else
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000422 Finder->addMatcher(ifStmt(isExpansionInMainFile(),
423 unless(hasParent(ifStmt())),
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000424 hasThen(returnsBool(Value, ThenLiteralId)),
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000425 hasElse(returnsBool(!Value)))
426 .bind(Id),
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000427 this);
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000428}
429
430void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder,
431 bool Value, StringRef Id) {
432 auto SimpleThen = binaryOperator(
433 hasOperatorName("="),
434 hasLHS(declRefExpr(hasDeclaration(decl().bind(IfAssignObjId)))),
435 hasLHS(expr().bind(IfAssignVariableId)),
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000436 hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId)));
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000437 auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1),
438 hasAnySubstatement(SimpleThen)));
439 auto SimpleElse = binaryOperator(
440 hasOperatorName("="),
441 hasLHS(declRefExpr(hasDeclaration(equalsBoundNode(IfAssignObjId)))),
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000442 hasRHS(cxxBoolLiteral(equals(!Value))));
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000443 auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
444 hasAnySubstatement(SimpleElse)));
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000445 if (ChainedConditionalAssignment)
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000446 Finder->addMatcher(
447 ifStmt(isExpansionInMainFile(), hasThen(Then), hasElse(Else)).bind(Id),
448 this);
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000449 else
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000450 Finder->addMatcher(ifStmt(isExpansionInMainFile(),
451 unless(hasParent(ifStmt())), hasThen(Then),
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000452 hasElse(Else))
453 .bind(Id),
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000454 this);
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000455}
456
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000457void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
458 bool Value,
459 StringRef Id) {
460 Finder->addMatcher(
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000461 compoundStmt(allOf(hasAnySubstatement(ifStmt(hasThen(returnsBool(Value)),
462 unless(hasElse(stmt())))),
463 hasAnySubstatement(
Piotr Padlewskie93a73f2016-05-31 15:26:56 +0000464 returnStmt(has(ignoringParenImpCasts(
465 cxxBoolLiteral(equals(!Value)))))
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000466 .bind(CompoundReturnId))))
467 .bind(Id),
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000468 this);
469}
470
Alexander Kornienkofb3e2cd2015-05-17 12:31:12 +0000471void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
472 Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
473 Options.store(Opts, "ChainedConditionalAssignment",
474 ChainedConditionalAssignment);
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000475}
476
477void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) {
478 matchBoolBinOpExpr(Finder, true, "&&", RightExpressionId);
479 matchBoolBinOpExpr(Finder, false, "||", RightExpressionId);
480 matchExprBinOpBool(Finder, false, "&&", RightExpressionId);
481 matchExprBinOpBool(Finder, true, "||", RightExpressionId);
482 matchBoolCompOpExpr(Finder, true, "==", RightExpressionId);
483 matchBoolCompOpExpr(Finder, false, "!=", RightExpressionId);
484
485 matchExprBinOpBool(Finder, true, "&&", LeftExpressionId);
486 matchExprBinOpBool(Finder, false, "||", LeftExpressionId);
487 matchBoolBinOpExpr(Finder, false, "&&", LeftExpressionId);
488 matchBoolBinOpExpr(Finder, true, "||", LeftExpressionId);
489 matchExprCompOpBool(Finder, true, "==", LeftExpressionId);
490 matchExprCompOpBool(Finder, false, "!=", LeftExpressionId);
491
492 matchBoolCompOpExpr(Finder, false, "==", NegatedRightExpressionId);
493 matchBoolCompOpExpr(Finder, true, "!=", NegatedRightExpressionId);
494
495 matchExprCompOpBool(Finder, false, "==", NegatedLeftExpressionId);
496 matchExprCompOpBool(Finder, true, "!=", NegatedLeftExpressionId);
497
498 matchBoolCondition(Finder, true, ConditionThenStmtId);
499 matchBoolCondition(Finder, false, ConditionElseStmtId);
500
501 matchTernaryResult(Finder, true, TernaryId);
502 matchTernaryResult(Finder, false, TernaryNegatedId);
503
504 matchIfReturnsBool(Finder, true, IfReturnsBoolId);
505 matchIfReturnsBool(Finder, false, IfReturnsNotBoolId);
506
507 matchIfAssignsBool(Finder, true, IfAssignBoolId);
508 matchIfAssignsBool(Finder, false, IfAssignNotBoolId);
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000509
510 matchCompoundIfReturnsBool(Finder, true, CompoundBoolId);
511 matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId);
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000512}
513
514void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
515 if (const CXXBoolLiteralExpr *LeftRemoved =
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000516 getBoolLiteral(Result, RightExpressionId))
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000517 replaceWithExpression(Result, LeftRemoved, false);
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000518 else if (const CXXBoolLiteralExpr *RightRemoved =
519 getBoolLiteral(Result, LeftExpressionId))
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000520 replaceWithExpression(Result, RightRemoved, true);
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000521 else if (const CXXBoolLiteralExpr *NegatedLeftRemoved =
522 getBoolLiteral(Result, NegatedRightExpressionId))
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000523 replaceWithExpression(Result, NegatedLeftRemoved, false, true);
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000524 else if (const CXXBoolLiteralExpr *NegatedRightRemoved =
525 getBoolLiteral(Result, NegatedLeftExpressionId))
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000526 replaceWithExpression(Result, NegatedRightRemoved, true, true);
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000527 else if (const CXXBoolLiteralExpr *TrueConditionRemoved =
528 getBoolLiteral(Result, ConditionThenStmtId))
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000529 replaceWithThenStatement(Result, TrueConditionRemoved);
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000530 else if (const CXXBoolLiteralExpr *FalseConditionRemoved =
531 getBoolLiteral(Result, ConditionElseStmtId))
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000532 replaceWithElseStatement(Result, FalseConditionRemoved);
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000533 else if (const auto *Ternary =
534 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId))
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000535 replaceWithCondition(Result, Ternary);
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000536 else if (const auto *TernaryNegated =
537 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId))
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000538 replaceWithCondition(Result, TernaryNegated, true);
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000539 else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId))
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000540 replaceWithReturnCondition(Result, If);
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000541 else if (const auto *IfNot =
542 Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId))
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000543 replaceWithReturnCondition(Result, IfNot, true);
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000544 else if (const auto *IfAssign =
545 Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId))
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000546 replaceWithAssignment(Result, IfAssign);
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000547 else if (const auto *IfAssignNot =
548 Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId))
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000549 replaceWithAssignment(Result, IfAssignNot, true);
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000550 else if (const auto *Compound =
551 Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId))
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000552 replaceCompoundReturnWithCondition(Result, Compound);
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000553 else if (const auto *Compound =
554 Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId))
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000555 replaceCompoundReturnWithCondition(Result, Compound, true);
Alexander Kornienko4f74ec02015-12-28 13:21:22 +0000556}
557
558void SimplifyBooleanExprCheck::issueDiag(
559 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc,
560 StringRef Description, SourceRange ReplacementRange,
561 StringRef Replacement) {
Gabor Horvathafad84c2016-09-24 02:13:45 +0000562 CharSourceRange CharRange =
563 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange),
564 *Result.SourceManager, getLangOpts());
Alexander Kornienko4f74ec02015-12-28 13:21:22 +0000565
566 DiagnosticBuilder Diag = diag(Loc, Description);
567 if (!containsDiscardedTokens(Result, CharRange))
568 Diag << FixItHint::CreateReplacement(CharRange, Replacement);
569}
570
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000571void SimplifyBooleanExprCheck::replaceWithExpression(
572 const ast_matchers::MatchFinder::MatchResult &Result,
573 const CXXBoolLiteralExpr *BoolLiteral, bool UseLHS, bool Negated) {
574 const auto *LHS = Result.Nodes.getNodeAs<Expr>(LHSId);
575 const auto *RHS = Result.Nodes.getNodeAs<Expr>(RHSId);
576 std::string Replacement =
577 replacementExpression(Result, Negated, UseLHS ? LHS : RHS);
Alexander Kornienko4f74ec02015-12-28 13:21:22 +0000578 SourceRange Range(LHS->getLocStart(), RHS->getLocEnd());
579 issueDiag(Result, BoolLiteral->getLocStart(), SimplifyOperatorDiagnostic,
580 Range, Replacement);
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000581}
582
583void SimplifyBooleanExprCheck::replaceWithThenStatement(
584 const MatchFinder::MatchResult &Result,
585 const CXXBoolLiteralExpr *TrueConditionRemoved) {
586 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
Alexander Kornienko4f74ec02015-12-28 13:21:22 +0000587 issueDiag(Result, TrueConditionRemoved->getLocStart(),
588 SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
589 getText(Result, *IfStatement->getThen()));
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000590}
591
592void SimplifyBooleanExprCheck::replaceWithElseStatement(
593 const MatchFinder::MatchResult &Result,
594 const CXXBoolLiteralExpr *FalseConditionRemoved) {
595 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
596 const Stmt *ElseStatement = IfStatement->getElse();
Alexander Kornienko4f74ec02015-12-28 13:21:22 +0000597 issueDiag(Result, FalseConditionRemoved->getLocStart(),
598 SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
599 ElseStatement ? getText(Result, *ElseStatement) : "");
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000600}
601
602void SimplifyBooleanExprCheck::replaceWithCondition(
603 const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary,
604 bool Negated) {
605 std::string Replacement =
606 replacementExpression(Result, Negated, Ternary->getCond());
Alexander Kornienko4f74ec02015-12-28 13:21:22 +0000607 issueDiag(Result, Ternary->getTrueExpr()->getLocStart(),
608 "redundant boolean literal in ternary expression result",
609 Ternary->getSourceRange(), Replacement);
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000610}
611
612void SimplifyBooleanExprCheck::replaceWithReturnCondition(
613 const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) {
614 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : "";
615 std::string Condition = replacementExpression(Result, Negated, If->getCond());
616 std::string Replacement = ("return " + Condition + Terminator).str();
617 SourceLocation Start =
618 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getLocStart();
Alexander Kornienko4f74ec02015-12-28 13:21:22 +0000619 issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic,
620 If->getSourceRange(), Replacement);
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000621}
622
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000623void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
624 const MatchFinder::MatchResult &Result, const CompoundStmt *Compound,
625 bool Negated) {
626 const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId);
627
628 // The body shouldn't be empty because the matcher ensures that it must
629 // contain at least two statements:
630 // 1) A `return` statement returning a boolean literal `false` or `true`
Aaron Ballmanf034a8c2016-02-12 15:09:05 +0000631 // 2) An `if` statement with no `else` clause that consists of a single
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000632 // `return` statement returning the opposite boolean literal `true` or
633 // `false`.
634 assert(Compound->size() >= 2);
635 const IfStmt *BeforeIf = nullptr;
636 CompoundStmt::const_body_iterator Current = Compound->body_begin();
637 CompoundStmt::const_body_iterator After = Compound->body_begin();
638 for (++After; After != Compound->body_end() && *Current != Ret;
639 ++Current, ++After) {
640 if (const auto *If = dyn_cast<IfStmt>(*Current)) {
641 if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) {
642 if (*After == Ret) {
643 if (!ChainedConditionalReturn && BeforeIf)
644 continue;
645
646 const Expr *Condition = If->getCond();
647 std::string Replacement =
648 "return " + replacementExpression(Result, Negated, Condition);
Alexander Kornienko4f74ec02015-12-28 13:21:22 +0000649 issueDiag(
650 Result, Lit->getLocStart(), SimplifyConditionalReturnDiagnostic,
651 SourceRange(If->getLocStart(), Ret->getLocEnd()), Replacement);
Alexander Kornienko6ae400d2015-07-01 12:39:40 +0000652 return;
653 }
654
655 BeforeIf = If;
656 }
657 } else {
658 BeforeIf = nullptr;
659 }
660 }
661}
662
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000663void SimplifyBooleanExprCheck::replaceWithAssignment(
664 const MatchFinder::MatchResult &Result, const IfStmt *IfAssign,
665 bool Negated) {
666 SourceRange Range = IfAssign->getSourceRange();
667 StringRef VariableName =
668 getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId));
669 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : "";
670 std::string Condition =
671 replacementExpression(Result, Negated, IfAssign->getCond());
672 std::string Replacement =
673 (VariableName + " = " + Condition + Terminator).str();
674 SourceLocation Location =
675 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getLocStart();
Alexander Kornienko4f74ec02015-12-28 13:21:22 +0000676 issueDiag(Result, Location,
677 "redundant boolean literal in conditional assignment", Range,
678 Replacement);
Alexander Kornienkof5e72b02015-04-10 19:26:43 +0000679}
680
681} // namespace readability
682} // namespace tidy
683} // namespace clang