LegalizeAdulthood updated this revision to Diff 426163.
LegalizeAdulthood added a comment.

Surround replacements with parens


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D124650/new/

https://reviews.llvm.org/D124650

Files:
  clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
  clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/docs/clang-tidy/checks/readability-simplify-boolean-expr.rst
  
clang-tools-extra/test/clang-tidy/checkers/readability-simplify-bool-expr-demorgan.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/readability-simplify-bool-expr-demorgan.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/readability-simplify-bool-expr-demorgan.cpp
@@ -0,0 +1,24 @@
+// RUN: %check_clang_tidy %s readability-simplify-boolean-expr %t -- -config="{CheckOptions: [{key: "readability-simplify-boolean-expr.SimplifyDeMorgan", value: true}]}" --
+
+bool a1 = false;
+bool a2 = false;
+
+bool aa = !(!a1 || a2);
+bool ab = !(a1 || !a2);
+bool ac = !(!a1 || !a2);
+// CHECK-MESSAGES: :[[@LINE-3]]:11: warning: boolean expression can be simplified by DeMorgan's theorem [readability-simplify-boolean-expr]
+// CHECK-MESSAGES: :[[@LINE-3]]:11: warning: boolean expression can be simplified by DeMorgan's theorem [readability-simplify-boolean-expr]
+// CHECK-MESSAGES: :[[@LINE-3]]:11: warning: boolean expression can be simplified by DeMorgan's theorem [readability-simplify-boolean-expr]
+// CHECK-FIXES: {{^bool aa = (a1 && !a2);$}}
+// CHECK-FIXES-NEXT: {{^bool ab = (!a1 && a2);$}}
+// CHECK-FIXES-NEXT: {{^bool ac = (a1 && a2);$}}
+
+bool ba = !(!a1 && a2);
+bool bb = !(a1 && !a2);
+bool bc = !(!a1 && !a2);
+// CHECK-MESSAGES: :[[@LINE-3]]:11: warning: boolean expression can be simplified by DeMorgan's theorem [readability-simplify-boolean-expr]
+// CHECK-MESSAGES: :[[@LINE-3]]:11: warning: boolean expression can be simplified by DeMorgan's theorem [readability-simplify-boolean-expr]
+// CHECK-MESSAGES: :[[@LINE-3]]:11: warning: boolean expression can be simplified by DeMorgan's theorem [readability-simplify-boolean-expr]
+// CHECK-FIXES: {{^bool ba = (a1 }}||{{ !a2);$}}
+// CHECK-FIXES-NEXT: {{^bool bb = (!a1 }}||{{ a2);$}}
+// CHECK-FIXES-NEXT: {{^bool bc = (a1 }}||{{ a2);$}}
Index: clang-tools-extra/docs/clang-tidy/checks/readability-simplify-boolean-expr.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/readability-simplify-boolean-expr.rst
+++ clang-tools-extra/docs/clang-tidy/checks/readability-simplify-boolean-expr.rst
@@ -4,7 +4,8 @@
 =================================
 
 Looks for boolean expressions involving boolean constants and simplifies
-them to use the appropriate boolean expression directly.
+them to use the appropriate boolean expression directly.  Simplifies
+boolean expressions by application of DeMorgan's Theorem.
 
 Examples:
 
@@ -27,6 +28,12 @@
 ``if (e) b = false; else b = true;``           ``b = !e;``
 ``if (e) return true; return false;``          ``return e;``
 ``if (e) return false; return true;``          ``return !e;``
+``!(!a || b)``                                 ``(a && !b)``
+``!(a || !b)``                                 ``(!a && b)``
+``!(!a || !b)``                                ``(a && b)``
+``!(!a && b)``                                 ``(a || !b)``
+``!(a && !b)``                                 ``(!a || b)``
+``!(!a && !b)``                                ``(a || b)``
 ===========================================  ================
 
 The resulting expression ``e`` is modified as follows:
@@ -84,3 +91,8 @@
 
    If `true`, conditional boolean assignments at the end of an ``if/else
    if`` chain will be transformed. Default is `false`.
+
+.. option:: SimplifyDeMorgan
+
+   If `true`, DeMorgan's Theorem will be applied to simplify negated
+   conjunctions and disjunctions.  Default is `true`.
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -136,6 +136,12 @@
 Changes in existing checks
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+- Fixed nonsensical suggestion of :doc:`altera-struct-pack-align
+  <clang-tidy/checks/altera-struct-pack-align>` check for empty structs.
+
+- Fixed some false positives in :doc:`bugprone-infinite-loop
+  <clang-tidy/checks/bugprone-infinite-loop>` involving dependent expressions.
+
 - Fixed a crash in :doc:`bugprone-sizeof-expression
   <clang-tidy/checks/bugprone-sizeof-expression>` when `sizeof(...)` is
   compared against a `__int128_t`.
@@ -158,26 +164,24 @@
   <clang-tidy/checks/misc-redundant-expression>` involving assignments in
   conditions. This fixes `Issue 35853 <https://github.com/llvm/llvm-project/issues/35853>`_.
 
-- Fixed a crash in :doc:`readability-const-return-type
-  <clang-tidy/checks/readability-const-return-type>` when a pure virtual function
-  overrided has a const return type. Removed the fix for a virtual function.
-
-- Fixed a false positive in :doc:`readability-non-const-parameter
-  <clang-tidy/checks/readability-non-const-parameter>` when the parameter is
-  referenced by an lvalue.
-
 - Improved :doc:`performance-inefficient-vector-operation
   <clang-tidy/checks/performance-inefficient-vector-operation>` to work when
   the vector is a member of a structure.
 
-- Fixed nonsensical suggestion of :doc:`altera-struct-pack-align
-  <clang-tidy/checks/altera-struct-pack-align>` check for empty structs.
+- Fixed a crash in :doc:`readability-const-return-type
+  <clang-tidy/checks/readability-const-return-type>` when a pure virtual function
+  overrided has a const return type. Removed the fix for a virtual function.
 
 - Fixed incorrect suggestions for :doc:`readability-container-size-empty
   <clang-tidy/checks/readability-container-size-empty>` when smart pointers are involved.
 
-- Fixed some false positives in :doc:`bugprone-infinite-loop
-  <clang-tidy/checks/bugprone-infinite-loop>` involving dependent expressions.
+- Fixed a false positive in :doc:`readability-non-const-parameter
+  <clang-tidy/checks/readability-non-const-parameter>` when the parameter is
+  referenced by an lvalue.
+
+- Expanded :doc:`readability-simplify-boolean-expr
+  <clang-tidy/checks/readability-simplify-boolean-expr>` to simplify expressions
+  using DeMorgan's Theorem.
 
 Removed checks
 ^^^^^^^^^^^^^^
Index: clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h
===================================================================
--- clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h
+++ clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.h
@@ -61,6 +61,8 @@
   void matchLabelIfReturnsBool(ast_matchers::MatchFinder *Finder, bool Value,
                                StringRef Id);
 
+  void matchDeMorganExpr(ast_matchers::MatchFinder *Finder);
+
   void
   replaceWithThenStatement(const ast_matchers::MatchFinder::MatchResult &Result,
                            const Expr *BoolLiteral);
@@ -77,10 +79,6 @@
       const ast_matchers::MatchFinder::MatchResult &Result, const IfStmt *If,
       bool Negated);
 
-  void
-  replaceWithAssignment(const ast_matchers::MatchFinder::MatchResult &Result,
-                        const IfStmt *If, bool Negated);
-
   void replaceCompoundReturnWithCondition(
       const ast_matchers::MatchFinder::MatchResult &Result,
       const CompoundStmt *Compound, bool Negated);
@@ -98,12 +96,22 @@
   void replaceLabelCompoundReturnWithCondition(
       const ast_matchers::MatchFinder::MatchResult &Result, bool Negated);
 
+  void
+  replaceWithAssignment(const ast_matchers::MatchFinder::MatchResult &Result,
+                        const IfStmt *If, bool Negated);
+
+  bool
+  simplifyDeMorganExpr(const ast_matchers::MatchFinder::MatchResult &Result,
+                       const Expr *OrLeft, bool NotLeft, StringRef Op,
+                       bool NotRight);
+
   void issueDiag(const ast_matchers::MatchFinder::MatchResult &Result,
                  SourceLocation Loc, StringRef Description,
                  SourceRange ReplacementRange, StringRef Replacement);
 
   const bool ChainedConditionalReturn;
   const bool ChainedConditionalAssignment;
+  const bool SimplifyDeMorgan;
 };
 
 } // namespace readability
Index: clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
===================================================================
--- clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
+++ clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
@@ -61,6 +61,14 @@
 static constexpr char LabelCompoundBoolId[] = "label-compound-bool";
 static constexpr char LabelCompoundNotBoolId[] = "label-compound-bool-not";
 static constexpr char IfStmtId[] = "if";
+static constexpr char LeftId[] = "left";
+static constexpr char RightId[] = "right";
+static constexpr char DeMorganOrLeftId[] = "demorgan-or-left";
+static constexpr char DeMorganOrRightId[] = "demorgan-or-right";
+static constexpr char DeMorganOrBothId[] = "demorgan-or-both";
+static constexpr char DeMorganAndLeftId[] = "demorgan-and-left";
+static constexpr char DeMorganAndRightId[] = "demorgan-and-right";
+static constexpr char DeMorganAndBothId[] = "demorgan-and-both";
 
 static constexpr char SimplifyOperatorDiagnostic[] =
     "redundant boolean literal supplied to boolean operator";
@@ -371,7 +379,8 @@
     : ClangTidyCheck(Name, Context),
       ChainedConditionalReturn(Options.get("ChainedConditionalReturn", false)),
       ChainedConditionalAssignment(
-          Options.get("ChainedConditionalAssignment", false)) {}
+          Options.get("ChainedConditionalAssignment", false)),
+      SimplifyDeMorgan(Options.get("SimplifyDeMorgan", true)) {}
 
 static bool containsBoolLiteral(const Expr *E) {
   if (!E)
@@ -570,10 +579,60 @@
   Finder->addMatcher(CompoundStmt, this);
 }
 
+void SimplifyBooleanExprCheck::matchDeMorganExpr(MatchFinder *Finder) {
+  if (!SimplifyDeMorgan)
+    return;
+
+  auto Not = hasOperatorName("!");
+  auto Or = hasOperatorName("||");
+  auto And = hasOperatorName("&&");
+  auto LHSExpr = expr().bind(LeftId);
+  auto RHSExpr = expr().bind(RightId);
+  auto NotOp = unaryOperator(Not);
+  auto LHS = hasLHS(LHSExpr);
+  auto RHS = hasRHS(RHSExpr);
+  auto NotLHS = hasLHS(unaryOperator(Not, hasUnaryOperand(LHSExpr)));
+  auto NotRHS = hasRHS(unaryOperator(Not, hasUnaryOperand(RHSExpr)));
+  auto UnlessNotRHS = unless(hasRHS(NotOp));
+  auto UnlessNotLHS = unless(hasLHS(NotOp));
+  // match !(!a || b)
+  Finder->addMatcher(unaryOperator(Not, hasUnaryOperand(binaryOperator(
+                                            Or, UnlessNotRHS, NotLHS, RHS)))
+                         .bind(DeMorganOrLeftId),
+                     this);
+  // match !(a || !b)
+  Finder->addMatcher(unaryOperator(Not, hasUnaryOperand(binaryOperator(
+                                            Or, UnlessNotLHS, LHS, NotRHS)))
+                         .bind(DeMorganOrRightId),
+                     this);
+  // match !(!a || !b)
+  Finder->addMatcher(
+      unaryOperator(Not, hasUnaryOperand(binaryOperator(Or, NotLHS, NotRHS)))
+          .bind(DeMorganOrBothId),
+      this);
+
+  // match !(!a && b)
+  Finder->addMatcher(unaryOperator(Not, hasUnaryOperand(binaryOperator(
+                                            And, UnlessNotRHS, NotLHS, RHS)))
+                         .bind(DeMorganAndLeftId),
+                     this);
+  // match !(a && !b)
+  Finder->addMatcher(unaryOperator(Not, hasUnaryOperand(binaryOperator(
+                                            And, UnlessNotLHS, LHS, NotRHS)))
+                         .bind(DeMorganAndRightId),
+                     this);
+  // match !(!a && !b)
+  Finder->addMatcher(
+      unaryOperator(Not, hasUnaryOperand(binaryOperator(And, NotLHS, NotRHS)))
+          .bind(DeMorganAndBothId),
+      this);
+}
+
 void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
   Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
   Options.store(Opts, "ChainedConditionalAssignment",
                 ChainedConditionalAssignment);
+  Options.store(Opts, "SimplifyDeMorgan", SimplifyDeMorgan);
 }
 
 void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) {
@@ -602,6 +661,8 @@
 
   matchLabelIfReturnsBool(Finder, true, LabelCompoundBoolId);
   matchLabelIfReturnsBool(Finder, false, LabelCompoundNotBoolId);
+
+  matchDeMorganExpr(Finder);
 }
 
 void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
@@ -650,6 +711,22 @@
     replaceLabelCompoundReturnWithCondition(Result, true);
   else if (const auto TU = Result.Nodes.getNodeAs<Decl>("top"))
     Visitor(this, Result).TraverseDecl(const_cast<Decl *>(TU));
+  else if (const auto *OrLeft = Result.Nodes.getNodeAs<Expr>(DeMorganOrLeftId))
+    simplifyDeMorganExpr(Result, OrLeft, false, "&&", true);
+  else if (const auto *OrRight =
+               Result.Nodes.getNodeAs<Expr>(DeMorganOrRightId))
+    simplifyDeMorganExpr(Result, OrRight, true, "&&", false);
+  else if (const auto *OrBoth = Result.Nodes.getNodeAs<Expr>(DeMorganOrBothId))
+    simplifyDeMorganExpr(Result, OrBoth, false, "&&", false);
+  else if (const auto *AndLeft =
+               Result.Nodes.getNodeAs<Expr>(DeMorganAndLeftId))
+    simplifyDeMorganExpr(Result, AndLeft, false, "||", true);
+  else if (const auto *AndRight =
+               Result.Nodes.getNodeAs<Expr>(DeMorganAndRightId))
+    simplifyDeMorganExpr(Result, AndRight, true, "||", false);
+  else if (const auto *AndBoth =
+               Result.Nodes.getNodeAs<Expr>(DeMorganAndBothId))
+    simplifyDeMorganExpr(Result, AndBoth, false, "||", false);
 }
 
 void SimplifyBooleanExprCheck::issueDiag(const MatchFinder::MatchResult &Result,
@@ -787,6 +864,26 @@
             Replacement);
 }
 
+bool SimplifyBooleanExprCheck::simplifyDeMorganExpr(
+    const MatchFinder::MatchResult &Result, const Expr *OrLeft, bool NotLeft,
+    StringRef Op, bool NotRight) {
+  if (!SimplifyDeMorgan)
+    return true;
+
+  const Expr *Left = Result.Nodes.getNodeAs<Expr>(LeftId);
+  const Expr *Right = Result.Nodes.getNodeAs<Expr>(RightId);
+  std::string Replacement =
+      (StringRef{"("} + (NotLeft ? "!" : "") + getText(Result, *Left) + " " +
+       Op + " " + (NotRight ? "!" : "") + getText(Result, *Right) + ")")
+          .str();
+  SourceLocation Loc = OrLeft->getBeginLoc();
+  SourceRange Range = OrLeft->getSourceRange();
+  issueDiag(Result, Loc,
+            "boolean expression can be simplified by DeMorgan's theorem", Range,
+            Replacement);
+  return false;
+}
+
 } // namespace readability
 } // namespace tidy
 } // namespace clang
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to