Bug Summary

File:clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
Warning:line 405, column 43
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name SimplifyBooleanExprCheck.cpp -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=none -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/build-llvm/tools/clang/stage2-bins -resource-dir /usr/lib/llvm-14/lib/clang/14.0.0 -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I tools/clang/tools/extra/clang-tidy/readability -I /build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/clang-tools-extra/clang-tidy/readability -I tools/clang/tools/extra/clang-tidy -I /build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/clang/include -I tools/clang/include -I include -I /build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/llvm/include -D _FORTIFY_SOURCE=2 -D NDEBUG -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/lib/llvm-14/lib/clang/14.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fmacro-prefix-map=/build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/= -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/= -O3 -Wno-unused-command-line-argument -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wno-comment -std=c++14 -fdeprecated-macro -fdebug-compilation-dir=/build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/= -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-01-25-232935-20746-1 -x c++ /build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
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
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 ConditionThenStmtId[] = "if-bool-yields-then";
37const char ConditionElseStmtId[] = "if-bool-yields-else";
38const char TernaryId[] = "ternary-bool-yields-condition";
39const char TernaryNegatedId[] = "ternary-bool-yields-not-condition";
40const char IfReturnsBoolId[] = "if-return";
41const char IfReturnsNotBoolId[] = "if-not-return";
42const char ThenLiteralId[] = "then-literal";
43const char IfAssignVariableId[] = "if-assign-lvalue";
44const char IfAssignLocId[] = "if-assign-loc";
45const char IfAssignBoolId[] = "if-assign";
46const char IfAssignNotBoolId[] = "if-assign-not";
47const char IfAssignVarId[] = "if-assign-var";
48const char CompoundReturnId[] = "compound-return";
49const char CompoundBoolId[] = "compound-bool";
50const char CompoundNotBoolId[] = "compound-bool-not";
51
52const char IfStmtId[] = "if";
53
54const char SimplifyOperatorDiagnostic[] =
55 "redundant boolean literal supplied to boolean operator";
56const char SimplifyConditionDiagnostic[] =
57 "redundant boolean literal in if statement condition";
58const char SimplifyConditionalReturnDiagnostic[] =
59 "redundant boolean literal in conditional return statement";
60
61const Expr *getBoolLiteral(const MatchFinder::MatchResult &Result,
62 StringRef Id) {
63 if (const Expr *Literal = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(Id))
64 return Literal->getBeginLoc().isMacroID() ? nullptr : Literal;
65 if (const auto *Negated = Result.Nodes.getNodeAs<UnaryOperator>(Id)) {
66 if (Negated->getOpcode() == UO_LNot &&
67 isa<CXXBoolLiteralExpr>(Negated->getSubExpr()))
68 return Negated->getBeginLoc().isMacroID() ? nullptr : Negated;
69 }
70 return nullptr;
71}
72
73internal::BindableMatcher<Stmt> literalOrNegatedBool(bool Value) {
74 return expr(
75 anyOf(cxxBoolLiteral(equals(Value)),
76 unaryOperator(hasUnaryOperand(cxxBoolLiteral(equals(!Value))),
77 hasOperatorName("!"))));
78}
79
80internal::Matcher<Stmt> returnsBool(bool Value, StringRef Id = "ignored") {
81 auto SimpleReturnsBool = returnStmt(has(literalOrNegatedBool(Value).bind(Id)))
82 .bind("returns-bool");
83 return anyOf(SimpleReturnsBool,
84 compoundStmt(statementCountIs(1), has(SimpleReturnsBool)));
85}
86
87bool needsParensAfterUnaryNegation(const Expr *E) {
88 E = E->IgnoreImpCasts();
89 if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E))
90 return true;
91
92 if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E))
93 return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
94 Op->getOperator() != OO_Subscript;
95
96 return false;
97}
98
99std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = {
100 {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
101
102StringRef negatedOperator(const BinaryOperator *BinOp) {
103 const BinaryOperatorKind Opcode = BinOp->getOpcode();
104 for (auto NegatableOp : Opposites) {
105 if (Opcode == NegatableOp.first)
106 return BinOp->getOpcodeStr(NegatableOp.second);
107 if (Opcode == NegatableOp.second)
108 return BinOp->getOpcodeStr(NegatableOp.first);
109 }
110 return StringRef();
111}
112
113std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = {
114 {OO_EqualEqual, "=="}, {OO_ExclaimEqual, "!="}, {OO_Less, "<"},
115 {OO_GreaterEqual, ">="}, {OO_Greater, ">"}, {OO_LessEqual, "<="}};
116
117StringRef getOperatorName(OverloadedOperatorKind OpKind) {
118 for (auto Name : OperatorNames) {
119 if (Name.first == OpKind)
120 return Name.second;
121 }
122
123 return StringRef();
124}
125
126std::pair<OverloadedOperatorKind, OverloadedOperatorKind> OppositeOverloads[] =
127 {{OO_EqualEqual, OO_ExclaimEqual},
128 {OO_Less, OO_GreaterEqual},
129 {OO_Greater, OO_LessEqual}};
130
131StringRef 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
142std::string asBool(StringRef Text, bool NeedsStaticCast) {
143 if (NeedsStaticCast)
144 return ("static_cast<bool>(" + Text + ")").str();
145
146 return std::string(Text);
147}
148
149bool needsNullPtrComparison(const Expr *E) {
150 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
151 return ImpCast->getCastKind() == CK_PointerToBoolean ||
152 ImpCast->getCastKind() == CK_MemberPointerToBoolean;
153
154 return false;
155}
156
157bool needsZeroComparison(const Expr *E) {
158 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
159 return ImpCast->getCastKind() == CK_IntegralToBoolean;
160
161 return false;
162}
163
164bool needsStaticCast(const Expr *E) {
165 if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {
166 if (ImpCast->getCastKind() == CK_UserDefinedConversion &&
167 ImpCast->getSubExpr()->getType()->isBooleanType()) {
168 if (const auto *MemCall =
169 dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) {
170 if (const auto *MemDecl =
171 dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) {
172 if (MemDecl->isExplicit())
173 return true;
174 }
175 }
176 }
177 }
178
179 E = E->IgnoreImpCasts();
180 return !E->getType()->isBooleanType();
181}
182
183std::string compareExpressionToConstant(const MatchFinder::MatchResult &Result,
184 const Expr *E, bool Negated,
185 const char *Constant) {
186 E = E->IgnoreImpCasts();
187 const std::string ExprText =
188 (isa<BinaryOperator>(E) ? ("(" + getText(Result, *E) + ")")
189 : getText(Result, *E))
190 .str();
191 return ExprText + " " + (Negated ? "!=" : "==") + " " + Constant;
192}
193
194std::string compareExpressionToNullPtr(const MatchFinder::MatchResult &Result,
195 const Expr *E, bool Negated) {
196 const char *NullPtr =
197 Result.Context->getLangOpts().CPlusPlus11 ? "nullptr" : "NULL";
198 return compareExpressionToConstant(Result, E, Negated, NullPtr);
199}
200
201std::string compareExpressionToZero(const MatchFinder::MatchResult &Result,
202 const Expr *E, bool Negated) {
203 return compareExpressionToConstant(Result, E, Negated, "0");
204}
205
206std::string replacementExpression(const MatchFinder::MatchResult &Result,
207 bool Negated, const Expr *E) {
208 E = E->IgnoreParenBaseCasts();
209 if (const auto *EC = dyn_cast<ExprWithCleanups>(E))
210 E = EC->getSubExpr();
211
212 const bool NeedsStaticCast = needsStaticCast(E);
213 if (Negated) {
214 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
215 if (UnOp->getOpcode() == UO_LNot) {
216 if (needsNullPtrComparison(UnOp->getSubExpr()))
217 return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), true);
218
219 if (needsZeroComparison(UnOp->getSubExpr()))
220 return compareExpressionToZero(Result, UnOp->getSubExpr(), true);
221
222 return replacementExpression(Result, false, UnOp->getSubExpr());
223 }
224 }
225
226 if (needsNullPtrComparison(E))
227 return compareExpressionToNullPtr(Result, E, false);
228
229 if (needsZeroComparison(E))
230 return compareExpressionToZero(Result, E, false);
231
232 StringRef NegatedOperator;
233 const Expr *LHS = nullptr;
234 const Expr *RHS = nullptr;
235 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
236 NegatedOperator = negatedOperator(BinOp);
237 LHS = BinOp->getLHS();
238 RHS = BinOp->getRHS();
239 } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
240 if (OpExpr->getNumArgs() == 2) {
241 NegatedOperator = negatedOperator(OpExpr);
242 LHS = OpExpr->getArg(0);
243 RHS = OpExpr->getArg(1);
244 }
245 }
246 if (!NegatedOperator.empty() && LHS && RHS)
247 return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " +
248 getText(Result, *RHS))
249 .str(),
250 NeedsStaticCast));
251
252 StringRef Text = getText(Result, *E);
253 if (!NeedsStaticCast && needsParensAfterUnaryNegation(E))
254 return ("!(" + Text + ")").str();
255
256 if (needsNullPtrComparison(E))
257 return compareExpressionToNullPtr(Result, E, false);
258
259 if (needsZeroComparison(E))
260 return compareExpressionToZero(Result, E, false);
261
262 return ("!" + asBool(Text, NeedsStaticCast));
263 }
264
265 if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
266 if (UnOp->getOpcode() == UO_LNot) {
267 if (needsNullPtrComparison(UnOp->getSubExpr()))
268 return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), false);
269
270 if (needsZeroComparison(UnOp->getSubExpr()))
271 return compareExpressionToZero(Result, UnOp->getSubExpr(), false);
272 }
273 }
274
275 if (needsNullPtrComparison(E))
276 return compareExpressionToNullPtr(Result, E, true);
277
278 if (needsZeroComparison(E))
279 return compareExpressionToZero(Result, E, true);
280
281 return asBool(getText(Result, *E), NeedsStaticCast);
282}
283
284const Expr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) {
285 if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) {
286 if (Bool->getValue() == !Negated)
287 return Bool;
288 }
289 if (const auto *Unary = dyn_cast<UnaryOperator>(Ret->getRetValue())) {
290 if (Unary->getOpcode() == UO_LNot) {
291 if (const auto *Bool =
292 dyn_cast<CXXBoolLiteralExpr>(Unary->getSubExpr())) {
293 if (Bool->getValue() == Negated)
294 return Bool;
295 }
296 }
297 }
298
299 return nullptr;
300}
301
302const Expr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) {
303 if (IfRet->getElse() != nullptr)
304 return nullptr;
305
306 if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen()))
307 return stmtReturnsBool(Ret, Negated);
308
309 if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) {
310 if (Compound->size() == 1) {
311 if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back()))
312 return stmtReturnsBool(CompoundRet, Negated);
313 }
314 }
315
316 return nullptr;
317}
318
319bool containsDiscardedTokens(const MatchFinder::MatchResult &Result,
320 CharSourceRange CharRange) {
321 std::string ReplacementText =
322 Lexer::getSourceText(CharRange, *Result.SourceManager,
323 Result.Context->getLangOpts())
324 .str();
325 Lexer Lex(CharRange.getBegin(), Result.Context->getLangOpts(),
326 ReplacementText.data(), ReplacementText.data(),
327 ReplacementText.data() + ReplacementText.size());
328 Lex.SetCommentRetentionState(true);
329
330 Token Tok;
331 while (!Lex.LexFromRawLexer(Tok)) {
332 if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash))
333 return true;
334 }
335
336 return false;
337}
338
339} // namespace
340
341class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
342 public:
343 Visitor(SimplifyBooleanExprCheck *Check,
344 const MatchFinder::MatchResult &Result)
345 : Check(Check), Result(Result) {}
346
347 bool VisitBinaryOperator(BinaryOperator *Op) {
348 Check->reportBinOp(Result, Op);
1
Calling 'SimplifyBooleanExprCheck::reportBinOp'
349 return true;
350 }
351
352 private:
353 SimplifyBooleanExprCheck *Check;
354 const MatchFinder::MatchResult &Result;
355};
356
357SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
358 ClangTidyContext *Context)
359 : ClangTidyCheck(Name, Context),
360 ChainedConditionalReturn(Options.get("ChainedConditionalReturn", false)),
361 ChainedConditionalAssignment(
362 Options.get("ChainedConditionalAssignment", false)) {}
363
364bool containsBoolLiteral(const Expr *E) {
365 if (!E)
366 return false;
367 E = E->IgnoreParenImpCasts();
368 if (isa<CXXBoolLiteralExpr>(E))
369 return true;
370 if (const auto *BinOp = dyn_cast<BinaryOperator>(E))
371 return containsBoolLiteral(BinOp->getLHS()) ||
372 containsBoolLiteral(BinOp->getRHS());
373 if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E))
374 return containsBoolLiteral(UnaryOp->getSubExpr());
375 return false;
376}
377
378void SimplifyBooleanExprCheck::reportBinOp(
379 const MatchFinder::MatchResult &Result, const BinaryOperator *Op) {
380 const auto *LHS = Op->getLHS()->IgnoreParenImpCasts();
381 const auto *RHS = Op->getRHS()->IgnoreParenImpCasts();
382
383 const CXXBoolLiteralExpr *Bool;
384 const Expr *Other = nullptr;
385 if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS)))
2
Assuming 'LHS' is a 'CXXBoolLiteralExpr'
3
Assuming 'Bool' is non-null
4
Taking true branch
386 Other = RHS;
387 else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS)))
388 Other = LHS;
389 else
390 return;
391
392 if (Bool->getBeginLoc().isMacroID())
393 return;
394
395 // FIXME: why do we need this?
396 if (!isa<CXXBoolLiteralExpr>(Other) && containsBoolLiteral(Other))
5
Assuming 'Other' is not a 'CXXBoolLiteralExpr'
6
Taking false branch
397 return;
398
399 bool BoolValue = Bool->getValue();
400
401 auto ReplaceWithExpression = [this, &Result, LHS, RHS,
7
Null pointer value stored to 'ReplaceWithExpression.'
402 Bool](const Expr *ReplaceWith, bool Negated) {
403 std::string Replacement =
404 replacementExpression(Result, Negated, ReplaceWith);
405 SourceRange Range(LHS->getBeginLoc(), RHS->getEndLoc());
10
Called C++ object pointer is null
406 issueDiag(Result, Bool->getBeginLoc(), SimplifyOperatorDiagnostic, Range,
407 Replacement);
408 };
409
410 switch (Op->getOpcode()) {
8
Control jumps to 'case BO_NE:' at line 433
411 case BO_LAnd:
412 if (BoolValue) {
413 // expr && true -> expr
414 ReplaceWithExpression(Other, /*Negated=*/false);
415 } else {
416 // expr && false -> false
417 ReplaceWithExpression(Bool, /*Negated=*/false);
418 }
419 break;
420 case BO_LOr:
421 if (BoolValue) {
422 // expr || true -> true
423 ReplaceWithExpression(Bool, /*Negated=*/false);
424 } else {
425 // expr || false -> expr
426 ReplaceWithExpression(Other, /*Negated=*/false);
427 }
428 break;
429 case BO_EQ:
430 // expr == true -> expr, expr == false -> !expr
431 ReplaceWithExpression(Other, /*Negated=*/!BoolValue);
432 break;
433 case BO_NE:
434 // expr != true -> !expr, expr != false -> expr
435 ReplaceWithExpression(Other, /*Negated=*/BoolValue);
9
Calling 'operator()'
436 break;
437 default:
438 break;
439 }
440}
441
442void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder,
443 bool Value,
444 StringRef BooleanId) {
445 Finder->addMatcher(
446 ifStmt(hasCondition(literalOrNegatedBool(Value).bind(BooleanId)))
447 .bind(IfStmtId),
448 this);
449}
450
451void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder,
452 bool Value,
453 StringRef TernaryId) {
454 Finder->addMatcher(
455 conditionalOperator(hasTrueExpression(literalOrNegatedBool(Value)),
456 hasFalseExpression(literalOrNegatedBool(!Value)))
457 .bind(TernaryId),
458 this);
459}
460
461void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder,
462 bool Value, StringRef Id) {
463 if (ChainedConditionalReturn)
464 Finder->addMatcher(ifStmt(hasThen(returnsBool(Value, ThenLiteralId)),
465 hasElse(returnsBool(!Value)))
466 .bind(Id),
467 this);
468 else
469 Finder->addMatcher(ifStmt(unless(hasParent(ifStmt())),
470 hasThen(returnsBool(Value, ThenLiteralId)),
471 hasElse(returnsBool(!Value)))
472 .bind(Id),
473 this);
474}
475
476void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder,
477 bool Value, StringRef Id) {
478 auto VarAssign = declRefExpr(hasDeclaration(decl().bind(IfAssignVarId)));
479 auto VarRef = declRefExpr(hasDeclaration(equalsBoundNode(IfAssignVarId)));
480 auto MemAssign = memberExpr(hasDeclaration(decl().bind(IfAssignVarId)));
481 auto MemRef = memberExpr(hasDeclaration(equalsBoundNode(IfAssignVarId)));
482 auto SimpleThen =
483 binaryOperator(hasOperatorName("="), hasLHS(anyOf(VarAssign, MemAssign)),
484 hasLHS(expr().bind(IfAssignVariableId)),
485 hasRHS(literalOrNegatedBool(Value).bind(IfAssignLocId)));
486 auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1),
487 hasAnySubstatement(SimpleThen)));
488 auto SimpleElse =
489 binaryOperator(hasOperatorName("="), hasLHS(anyOf(VarRef, MemRef)),
490 hasRHS(literalOrNegatedBool(!Value)));
491 auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
492 hasAnySubstatement(SimpleElse)));
493 if (ChainedConditionalAssignment)
494 Finder->addMatcher(ifStmt(hasThen(Then), hasElse(Else)).bind(Id), this);
495 else
496 Finder->addMatcher(
497 ifStmt(unless(hasParent(ifStmt())), hasThen(Then), hasElse(Else))
498 .bind(Id),
499 this);
500}
501
502void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
503 bool Value,
504 StringRef Id) {
505 Finder->addMatcher(
506 compoundStmt(
507 hasAnySubstatement(
508 ifStmt(hasThen(returnsBool(Value)), unless(hasElse(stmt())))),
509 hasAnySubstatement(returnStmt(has(literalOrNegatedBool(!Value)))
510 .bind(CompoundReturnId)))
511 .bind(Id),
512 this);
513}
514
515void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
516 Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
517 Options.store(Opts, "ChainedConditionalAssignment",
518 ChainedConditionalAssignment);
519}
520
521void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) {
522 Finder->addMatcher(translationUnitDecl().bind("top"), this);
523
524 matchBoolCondition(Finder, true, ConditionThenStmtId);
525 matchBoolCondition(Finder, false, ConditionElseStmtId);
526
527 matchTernaryResult(Finder, true, TernaryId);
528 matchTernaryResult(Finder, false, TernaryNegatedId);
529
530 matchIfReturnsBool(Finder, true, IfReturnsBoolId);
531 matchIfReturnsBool(Finder, false, IfReturnsNotBoolId);
532
533 matchIfAssignsBool(Finder, true, IfAssignBoolId);
534 matchIfAssignsBool(Finder, false, IfAssignNotBoolId);
535
536 matchCompoundIfReturnsBool(Finder, true, CompoundBoolId);
537 matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId);
538}
539
540void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
541 if (Result.Nodes.getNodeAs<TranslationUnitDecl>("top"))
542 Visitor(this, Result).TraverseAST(*Result.Context);
543 else if (const Expr *TrueConditionRemoved =
544 getBoolLiteral(Result, ConditionThenStmtId))
545 replaceWithThenStatement(Result, TrueConditionRemoved);
546 else if (const Expr *FalseConditionRemoved =
547 getBoolLiteral(Result, ConditionElseStmtId))
548 replaceWithElseStatement(Result, FalseConditionRemoved);
549 else if (const auto *Ternary =
550 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId))
551 replaceWithCondition(Result, Ternary);
552 else if (const auto *TernaryNegated =
553 Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId))
554 replaceWithCondition(Result, TernaryNegated, true);
555 else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId))
556 replaceWithReturnCondition(Result, If);
557 else if (const auto *IfNot =
558 Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId))
559 replaceWithReturnCondition(Result, IfNot, true);
560 else if (const auto *IfAssign =
561 Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId))
562 replaceWithAssignment(Result, IfAssign);
563 else if (const auto *IfAssignNot =
564 Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId))
565 replaceWithAssignment(Result, IfAssignNot, true);
566 else if (const auto *Compound =
567 Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId))
568 replaceCompoundReturnWithCondition(Result, Compound);
569 else if (const auto *Compound =
570 Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId))
571 replaceCompoundReturnWithCondition(Result, Compound, true);
572}
573
574void SimplifyBooleanExprCheck::issueDiag(
575 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc,
576 StringRef Description, SourceRange ReplacementRange,
577 StringRef Replacement) {
578 CharSourceRange CharRange =
579 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange),
580 *Result.SourceManager, getLangOpts());
581
582 DiagnosticBuilder Diag = diag(Loc, Description);
583 if (!containsDiscardedTokens(Result, CharRange))
584 Diag << FixItHint::CreateReplacement(CharRange, Replacement);
585}
586
587void SimplifyBooleanExprCheck::replaceWithThenStatement(
588 const MatchFinder::MatchResult &Result, const Expr *TrueConditionRemoved) {
589 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
590 issueDiag(Result, TrueConditionRemoved->getBeginLoc(),
591 SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
592 getText(Result, *IfStatement->getThen()));
593}
594
595void SimplifyBooleanExprCheck::replaceWithElseStatement(
596 const MatchFinder::MatchResult &Result, const Expr *FalseConditionRemoved) {
597 const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
598 const Stmt *ElseStatement = IfStatement->getElse();
599 issueDiag(Result, FalseConditionRemoved->getBeginLoc(),
600 SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
601 ElseStatement ? getText(Result, *ElseStatement) : "");
602}
603
604void SimplifyBooleanExprCheck::replaceWithCondition(
605 const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary,
606 bool Negated) {
607 std::string Replacement =
608 replacementExpression(Result, Negated, Ternary->getCond());
609 issueDiag(Result, Ternary->getTrueExpr()->getBeginLoc(),
610 "redundant boolean literal in ternary expression result",
611 Ternary->getSourceRange(), Replacement);
612}
613
614void SimplifyBooleanExprCheck::replaceWithReturnCondition(
615 const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) {
616 StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : "";
617 std::string Condition = replacementExpression(Result, Negated, If->getCond());
618 std::string Replacement = ("return " + Condition + Terminator).str();
619 SourceLocation Start =
620 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getBeginLoc();
621 issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic,
622 If->getSourceRange(), Replacement);
623}
624
625void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
626 const MatchFinder::MatchResult &Result, const CompoundStmt *Compound,
627 bool Negated) {
628 const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId);
629
630 // The body shouldn't be empty because the matcher ensures that it must
631 // contain at least two statements:
632 // 1) A `return` statement returning a boolean literal `false` or `true`
633 // 2) An `if` statement with no `else` clause that consists of a single
634 // `return` statement returning the opposite boolean literal `true` or
635 // `false`.
636 assert(Compound->size() >= 2)(static_cast <bool> (Compound->size() >= 2) ? void
(0) : __assert_fail ("Compound->size() >= 2", "clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp"
, 636, __extension__ __PRETTY_FUNCTION__))
;
637 const IfStmt *BeforeIf = nullptr;
638 CompoundStmt::const_body_iterator Current = Compound->body_begin();
639 CompoundStmt::const_body_iterator After = Compound->body_begin();
640 for (++After; After != Compound->body_end() && *Current != Ret;
641 ++Current, ++After) {
642 if (const auto *If = dyn_cast<IfStmt>(*Current)) {
643 if (const Expr *Lit = stmtReturnsBool(If, Negated)) {
644 if (*After == Ret) {
645 if (!ChainedConditionalReturn && BeforeIf)
646 continue;
647
648 const Expr *Condition = If->getCond();
649 std::string Replacement =
650 "return " + replacementExpression(Result, Negated, Condition);
651 issueDiag(
652 Result, Lit->getBeginLoc(), SimplifyConditionalReturnDiagnostic,
653 SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement);
654 return;
655 }
656
657 BeforeIf = If;
658 }
659 } else {
660 BeforeIf = nullptr;
661 }
662 }
663}
664
665void SimplifyBooleanExprCheck::replaceWithAssignment(
666 const MatchFinder::MatchResult &Result, const IfStmt *IfAssign,
667 bool Negated) {
668 SourceRange Range = IfAssign->getSourceRange();
669 StringRef VariableName =
670 getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId));
671 StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : "";
672 std::string Condition =
673 replacementExpression(Result, Negated, IfAssign->getCond());
674 std::string Replacement =
675 (VariableName + " = " + Condition + Terminator).str();
676 SourceLocation Location =
677 Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getBeginLoc();
678 issueDiag(Result, Location,
679 "redundant boolean literal in conditional assignment", Range,
680 Replacement);
681}
682
683} // namespace readability
684} // namespace tidy
685} // namespace clang