diff --git a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h --- a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h +++ b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h @@ -69,6 +69,7 @@ const bool ChainedConditionalReturn; const bool ChainedConditionalAssignment; const bool SimplifyDeMorgan; + const bool SimplifyDeMorganRelaxed; }; } // namespace readability diff --git a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp --- a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp @@ -558,7 +558,8 @@ if (!BinaryOp || !BinaryOp->isLogicalOp() || !BinaryOp->getType()->isBooleanType()) return Base::TraverseUnaryOperator(Op); - if (checkEitherSide(BinaryOp, isUnaryLNot) || + if (Check->SimplifyDeMorganRelaxed || + checkEitherSide(BinaryOp, isUnaryLNot) || checkEitherSide(BinaryOp, [](const Expr *E) { return nestedDemorgan(E, 1); })) { if (Check->reportDeMorgan(Context, Op, BinaryOp, !IsProcessing, parent(), @@ -584,7 +585,13 @@ ChainedConditionalReturn(Options.get("ChainedConditionalReturn", false)), ChainedConditionalAssignment( Options.get("ChainedConditionalAssignment", false)), - SimplifyDeMorgan(Options.get("SimplifyDeMorgan", true)) {} + SimplifyDeMorgan(Options.get("SimplifyDeMorgan", true)), + SimplifyDeMorganRelaxed(Options.get("SimplifyDeMorganRelaxed", false)) { + if (SimplifyDeMorganRelaxed && !SimplifyDeMorgan) + configurationDiag("%0: 'SimplifyDeMorganRelaxed' cannot be enabled " + "without 'SimplifyDeMorgan' enabled") + << Name; +} static bool containsBoolLiteral(const Expr *E) { if (!E) @@ -667,6 +674,7 @@ Options.store(Opts, "ChainedConditionalAssignment", ChainedConditionalAssignment); Options.store(Opts, "SimplifyDeMorgan", SimplifyDeMorgan); + Options.store(Opts, "SimplifyDeMorganRelaxed", SimplifyDeMorganRelaxed); } void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) { diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability-simplify-boolean-expr.rst b/clang-tools-extra/docs/clang-tidy/checks/readability-simplify-boolean-expr.rst --- a/clang-tools-extra/docs/clang-tidy/checks/readability-simplify-boolean-expr.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/readability-simplify-boolean-expr.rst @@ -96,3 +96,23 @@ If `true`, DeMorgan's Theorem will be applied to simplify negated conjunctions and disjunctions. Default is `true`. + +.. option:: SimplifyDeMorganRelaxed + + If `true`, :option:`SimplifyDeMorgan` will also transform negated + conjunctions and disjunctions where there is no negation on either operand. + Default is `false`. + + When Enabled: + + .. code-block:: + + bool X = !(A && B) + bool Y = !(A || B) + + Would be transformed to: + + .. code-block:: + + bool X = !A || !B + bool Y = !A && !B diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability-simplify-bool-expr-demorgan.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability-simplify-bool-expr-demorgan.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/readability-simplify-bool-expr-demorgan.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability-simplify-bool-expr-demorgan.cpp @@ -1,9 +1,30 @@ // RUN: %check_clang_tidy %s readability-simplify-boolean-expr %t +// Check when we can convert !(A Op B) -> !A InvOp !B. +// RUN: %check_clang_tidy -check-suffixes=",RELAXED" %s \ +// RUN: readability-simplify-boolean-expr %t -- -config="{CheckOptions: [{ \ +// RUN: key: "readability-simplify-boolean-expr.SimplifyDeMorganRelaxed", value: true}]}" -- + +// Verify warning issued when invalid options are specified. +// RUN: clang-tidy %s -checks=-*,readability-simplify-boolean-expr -config="{CheckOptions: [ \ +// RUN: {key: readability-simplify-boolean-expr.SimplifyDeMorgan, value: false}, \ +// RUN: {key: readability-simplify-boolean-expr.SimplifyDeMorganRelaxed, value: true}]}" \ +// RUN: -- 2>&1 | FileCheck %s -check-prefix=CHECK-BAD-CONFIG \ +// RUN: -implicit-check-not="{{warning|error}}:" + +// CHECK-BAD-CONFIG: warning: readability-simplify-boolean-expr: 'SimplifyDeMorganRelaxed' cannot be enabled without 'SimplifyDeMorgan' enabled void eat(bool); void foo(bool A1, bool A2, bool A3, bool A4) { bool X; + + X = !(A1 && A2); + X = !(A1 || A2); + // CHECK-MESSAGES-RELAXED: :[[@LINE-2]]:7: warning: boolean expression can be simplified by DeMorgan's theorem + // CHECK-MESSAGES-RELAXED: :[[@LINE-2]]:7: warning: boolean expression can be simplified by DeMorgan's theorem + // CHECK-FIXES-RELAXED: X = !A1 || !A2; + // CHECK-FIXES-NEXT-RELAXED: X = !A1 && !A2; + X = !(!A1 || A2); X = !(A1 || !A2); X = !(!A1 || !A2);