Skip to content

Commit f3c1d28

Browse files
committed
[clang-tidy] Extend SimplifyBooleanExpr demorgan support.
Adds an option SimplifyDemorganRelaxed which, when enabled, will transform negated conjunctions or disjunctions when neither operand is a negation. Default value is `false`. Reviewed By: LegalizeAdulthood Differential Revision: https://reviews.llvm.org/D126162
1 parent 1ad9b26 commit f3c1d28

File tree

4 files changed

+52
-2
lines changed

4 files changed

+52
-2
lines changed

clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,8 @@ class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
558558
if (!BinaryOp || !BinaryOp->isLogicalOp() ||
559559
!BinaryOp->getType()->isBooleanType())
560560
return Base::TraverseUnaryOperator(Op);
561-
if (checkEitherSide(BinaryOp, isUnaryLNot) ||
561+
if (Check->SimplifyDeMorganRelaxed ||
562+
checkEitherSide(BinaryOp, isUnaryLNot) ||
562563
checkEitherSide(BinaryOp,
563564
[](const Expr *E) { return nestedDemorgan(E, 1); })) {
564565
if (Check->reportDeMorgan(Context, Op, BinaryOp, !IsProcessing, parent(),
@@ -584,7 +585,13 @@ SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
584585
ChainedConditionalReturn(Options.get("ChainedConditionalReturn", false)),
585586
ChainedConditionalAssignment(
586587
Options.get("ChainedConditionalAssignment", false)),
587-
SimplifyDeMorgan(Options.get("SimplifyDeMorgan", true)) {}
588+
SimplifyDeMorgan(Options.get("SimplifyDeMorgan", true)),
589+
SimplifyDeMorganRelaxed(Options.get("SimplifyDeMorganRelaxed", false)) {
590+
if (SimplifyDeMorganRelaxed && !SimplifyDeMorgan)
591+
configurationDiag("%0: 'SimplifyDeMorganRelaxed' cannot be enabled "
592+
"without 'SimplifyDeMorgan' enabled")
593+
<< Name;
594+
}
588595

589596
static bool containsBoolLiteral(const Expr *E) {
590597
if (!E)
@@ -667,6 +674,7 @@ void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
667674
Options.store(Opts, "ChainedConditionalAssignment",
668675
ChainedConditionalAssignment);
669676
Options.store(Opts, "SimplifyDeMorgan", SimplifyDeMorgan);
677+
Options.store(Opts, "SimplifyDeMorganRelaxed", SimplifyDeMorganRelaxed);
670678
}
671679

672680
void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) {

clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class SimplifyBooleanExprCheck : public ClangTidyCheck {
6969
const bool ChainedConditionalReturn;
7070
const bool ChainedConditionalAssignment;
7171
const bool SimplifyDeMorgan;
72+
const bool SimplifyDeMorganRelaxed;
7273
};
7374

7475
} // namespace readability

clang-tools-extra/docs/clang-tidy/checks/readability-simplify-boolean-expr.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,23 @@ Options
9696

9797
If `true`, DeMorgan's Theorem will be applied to simplify negated
9898
conjunctions and disjunctions. Default is `true`.
99+
100+
.. option:: SimplifyDeMorganRelaxed
101+
102+
If `true`, :option:`SimplifyDeMorgan` will also transform negated
103+
conjunctions and disjunctions where there is no negation on either operand.
104+
Default is `false`.
105+
106+
When Enabled:
107+
108+
.. code-block::
109+
110+
bool X = !(A && B)
111+
bool Y = !(A || B)
112+
113+
Would be transformed to:
114+
115+
.. code-block::
116+
117+
bool X = !A || !B
118+
bool Y = !A && !B

clang-tools-extra/test/clang-tidy/checkers/readability-simplify-bool-expr-demorgan.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
11
// RUN: %check_clang_tidy %s readability-simplify-boolean-expr %t
22

3+
// Check when we can convert !(A Op B) -> !A InvOp !B.
4+
// RUN: %check_clang_tidy -check-suffixes=",RELAXED" %s \
5+
// RUN: readability-simplify-boolean-expr %t -- -config="{CheckOptions: [{ \
6+
// RUN: key: "readability-simplify-boolean-expr.SimplifyDeMorganRelaxed", value: true}]}" --
7+
8+
// Verify warning issued when invalid options are specified.
9+
// RUN: clang-tidy %s -checks=-*,readability-simplify-boolean-expr -config="{CheckOptions: [ \
10+
// RUN: {key: readability-simplify-boolean-expr.SimplifyDeMorgan, value: false}, \
11+
// RUN: {key: readability-simplify-boolean-expr.SimplifyDeMorganRelaxed, value: true}]}" \
12+
// RUN: -- 2>&1 | FileCheck %s -check-prefix=CHECK-BAD-CONFIG \
13+
// RUN: -implicit-check-not="{{warning|error}}:"
14+
15+
// CHECK-BAD-CONFIG: warning: readability-simplify-boolean-expr: 'SimplifyDeMorganRelaxed' cannot be enabled without 'SimplifyDeMorgan' enabled
316
void eat(bool);
417

518
void foo(bool A1, bool A2, bool A3, bool A4) {
619
bool X;
20+
21+
X = !(A1 && A2);
22+
X = !(A1 || A2);
23+
// CHECK-MESSAGES-RELAXED: :[[@LINE-2]]:7: warning: boolean expression can be simplified by DeMorgan's theorem
24+
// CHECK-MESSAGES-RELAXED: :[[@LINE-2]]:7: warning: boolean expression can be simplified by DeMorgan's theorem
25+
// CHECK-FIXES-RELAXED: X = !A1 || !A2;
26+
// CHECK-FIXES-NEXT-RELAXED: X = !A1 && !A2;
27+
728
X = !(!A1 || A2);
829
X = !(A1 || !A2);
930
X = !(!A1 || !A2);

0 commit comments

Comments
 (0)