[clang-tools-extra] 14dd073 - [Clang-Tidy] New check `bugprone-redundant-branch-condition`

Adam Balogh via cfe-commits cfe-commits at lists.llvm.org
Mon Aug 31 07:02:10 PDT 2020


Author: Adam Balogh
Date: 2020-08-31T16:00:59+02:00
New Revision: 14dd0737822ba476803320a2ff37a1012174d312

URL: https://github.com/llvm/llvm-project/commit/14dd0737822ba476803320a2ff37a1012174d312
DIFF: https://github.com/llvm/llvm-project/commit/14dd0737822ba476803320a2ff37a1012174d312.diff

LOG: [Clang-Tidy] New check `bugprone-redundant-branch-condition`

Checking the same condition again in a nested `if` usually make no sense,
except if the value of the expression could have been changed between
the two checks. Although compilers may optimize this out, such code is
suspicious: the programmer may have meant to check something else.
Therefore it is worth to find such places in the code and notify the
user about the problem.

This patch implements a basic check for this problem. Currently it
only detects redundant conditions where the condition is a variable of
integral type. It also detects the possible bug if the variable is in an
//or// or //and// logical expression in the inner if and/or the variable
is in an //and// logical expression in the outer if statement. Negated
cases are not handled yet.

Differential Revision: https://reviews.llvm.org/D81272

Added: 
    clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.cpp
    clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.h
    clang-tools-extra/docs/clang-tidy/checks/bugprone-redundant-branch-condition.rst
    clang-tools-extra/test/clang-tidy/checkers/bugprone-redundant-branch-condition.cpp

Modified: 
    clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
    clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
    clang-tools-extra/docs/ReleaseNotes.rst
    clang-tools-extra/docs/clang-tidy/checks/list.rst

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 3f735a8484d8..1556a2924f59 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -38,6 +38,7 @@
 #include "NotNullTerminatedResultCheck.h"
 #include "ParentVirtualCallCheck.h"
 #include "PosixReturnCheck.h"
+#include "RedundantBranchConditionCheck.h"
 #include "ReservedIdentifierCheck.h"
 #include "SignedCharMisuseCheck.h"
 #include "SizeofContainerCheck.h"
@@ -119,6 +120,8 @@ class BugproneModule : public ClangTidyModule {
         "bugprone-move-forwarding-reference");
     CheckFactories.registerCheck<MultipleStatementMacroCheck>(
         "bugprone-multiple-statement-macro");
+    CheckFactories.registerCheck<RedundantBranchConditionCheck>(
+        "bugprone-redundant-branch-condition");
     CheckFactories.registerCheck<cppcoreguidelines::NarrowingConversionsCheck>(
         "bugprone-narrowing-conversions");
     CheckFactories.registerCheck<NoEscapeCheck>("bugprone-no-escape");

diff  --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index 6e7a94928a5a..169e0529d872 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -33,6 +33,7 @@ add_clang_library(clangTidyBugproneModule
   NotNullTerminatedResultCheck.cpp
   ParentVirtualCallCheck.cpp
   PosixReturnCheck.cpp
+  RedundantBranchConditionCheck.cpp
   ReservedIdentifierCheck.cpp
   SignedCharMisuseCheck.cpp
   SizeofContainerCheck.cpp

diff  --git a/clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.cpp
new file mode 100644
index 000000000000..137356acbdba
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.cpp
@@ -0,0 +1,153 @@
+//===--- RedundantBranchConditionCheck.cpp - clang-tidy -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantBranchConditionCheck.h"
+#include "../utils/Aliasing.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+using clang::tidy::utils::hasPtrOrReferenceInFunc;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+static const char CondVarStr[] = "cond_var";
+static const char OuterIfStr[] = "outer_if";
+static const char InnerIfStr[] = "inner_if";
+static const char FuncStr[] = "func";
+
+/// Returns whether `Var` is changed in `S` before `NextS`.
+static bool isChangedBefore(const Stmt *S, const Stmt *NextS,
+                            const VarDecl *Var, ASTContext *Context) {
+  ExprMutationAnalyzer MutAn(*S, *Context);
+  const auto &SM = Context->getSourceManager();
+  const Stmt *MutS = MutAn.findMutation(Var);
+  return MutS &&
+         SM.isBeforeInTranslationUnit(MutS->getEndLoc(), NextS->getBeginLoc());
+}
+
+void RedundantBranchConditionCheck::registerMatchers(MatchFinder *Finder) {
+  const auto ImmutableVar =
+      varDecl(anyOf(parmVarDecl(), hasLocalStorage()), hasType(isInteger()),
+              unless(hasType(isVolatileQualified())))
+          .bind(CondVarStr);
+  Finder->addMatcher(
+      ifStmt(
+          hasCondition(ignoringParenImpCasts(anyOf(
+              declRefExpr(hasDeclaration(ImmutableVar)),
+              binaryOperator(hasOperatorName("&&"),
+                             hasEitherOperand(ignoringParenImpCasts(declRefExpr(
+                                 hasDeclaration(ImmutableVar)))))))),
+          hasThen(hasDescendant(
+              ifStmt(hasCondition(ignoringParenImpCasts(
+                         anyOf(declRefExpr(hasDeclaration(
+                                   varDecl(equalsBoundNode(CondVarStr)))),
+                               binaryOperator(
+                                   hasAnyOperatorName("&&", "||"),
+                                   hasEitherOperand(ignoringParenImpCasts(
+                                       declRefExpr(hasDeclaration(varDecl(
+                                           equalsBoundNode(CondVarStr)))))))))))
+                  .bind(InnerIfStr))),
+          forFunction(functionDecl().bind(FuncStr)))
+          .bind(OuterIfStr),
+      this);
+  // FIXME: Handle longer conjunctive and disjunctive clauses.
+}
+
+void RedundantBranchConditionCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *OuterIf = Result.Nodes.getNodeAs<IfStmt>(OuterIfStr);
+  const auto *InnerIf = Result.Nodes.getNodeAs<IfStmt>(InnerIfStr);
+  const auto *CondVar = Result.Nodes.getNodeAs<VarDecl>(CondVarStr);
+  const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>(FuncStr);
+
+  // If the variable has an alias then it can be changed by that alias as well.
+  // FIXME: could potentially support tracking pointers and references in the
+  // future to improve catching true positives through aliases.
+  if (hasPtrOrReferenceInFunc(Func, CondVar))
+    return;
+
+  if (isChangedBefore(OuterIf->getThen(), InnerIf, CondVar, Result.Context))
+    return;
+
+  auto Diag = diag(InnerIf->getBeginLoc(), "redundant condition %0") << CondVar;
+
+  // For standalone condition variables and for "or" binary operations we simply
+  // remove the inner `if`.
+  const auto *BinOpCond = dyn_cast<BinaryOperator>(InnerIf->getCond());
+  if (isa<DeclRefExpr>(InnerIf->getCond()->IgnoreParenImpCasts()) ||
+      (BinOpCond && BinOpCond->getOpcode() == BO_LOr)) {
+    SourceLocation IfBegin = InnerIf->getBeginLoc();
+    const Stmt *Body = InnerIf->getThen();
+    const Expr *OtherSide = nullptr;
+    if (BinOpCond) {
+      const auto *LeftDRE =
+          dyn_cast<DeclRefExpr>(BinOpCond->getLHS()->IgnoreParenImpCasts());
+      if (LeftDRE && LeftDRE->getDecl() == CondVar)
+        OtherSide = BinOpCond->getRHS();
+      else
+        OtherSide = BinOpCond->getLHS();
+    }
+
+    SourceLocation IfEnd = Body->getBeginLoc().getLocWithOffset(-1);
+
+    // For compound statements also remove the left brace.
+    if (isa<CompoundStmt>(Body))
+      IfEnd = Body->getBeginLoc();
+
+    // If the other side has side effects then keep it.
+    if (OtherSide && OtherSide->HasSideEffects(*Result.Context)) {
+      SourceLocation BeforeOtherSide =
+          OtherSide->getBeginLoc().getLocWithOffset(-1);
+      SourceLocation AfterOtherSide =
+          Lexer::findNextToken(OtherSide->getEndLoc(), *Result.SourceManager,
+                               getLangOpts())
+              ->getLocation();
+      Diag << FixItHint::CreateRemoval(
+                  CharSourceRange::getTokenRange(IfBegin, BeforeOtherSide))
+           << FixItHint::CreateInsertion(AfterOtherSide, ";")
+           << FixItHint::CreateRemoval(
+                  CharSourceRange::getTokenRange(AfterOtherSide, IfEnd));
+    } else {
+      Diag << FixItHint::CreateRemoval(
+          CharSourceRange::getTokenRange(IfBegin, IfEnd));
+    }
+
+    // For comound statements also remove the right brace at the end.
+    if (isa<CompoundStmt>(Body))
+      Diag << FixItHint::CreateRemoval(
+          CharSourceRange::getTokenRange(Body->getEndLoc(), Body->getEndLoc()));
+
+    // For "and" binary operations we remove the "and" operation with the
+    // condition variable from the inner if.
+  } else {
+    const auto *CondOp = cast<BinaryOperator>(InnerIf->getCond());
+    const auto *LeftDRE =
+        dyn_cast<DeclRefExpr>(CondOp->getLHS()->IgnoreParenImpCasts());
+    if (LeftDRE && LeftDRE->getDecl() == CondVar) {
+      SourceLocation BeforeRHS =
+          CondOp->getRHS()->getBeginLoc().getLocWithOffset(-1);
+      Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
+          CondOp->getLHS()->getBeginLoc(), BeforeRHS));
+    } else {
+      SourceLocation AfterLHS =
+          Lexer::findNextToken(CondOp->getLHS()->getEndLoc(),
+                               *Result.SourceManager, getLangOpts())
+              ->getLocation();
+      Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
+          AfterLHS, CondOp->getRHS()->getEndLoc()));
+    }
+  }
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang

diff  --git a/clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.h b/clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.h
new file mode 100644
index 000000000000..7b38d59a0121
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.h
@@ -0,0 +1,35 @@
+//===--- RedundantBranchConditionCheck.h - clang-tidy -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_REDUNDANTBRANCHCONDITIONCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_REDUNDANTBRANCHCONDITIONCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Finds condition variables in nested `if` statements that were also checked
+/// in the outer `if` statement and were not changed.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-redundant-branch-condition.html
+class RedundantBranchConditionCheck : public ClangTidyCheck {
+public:
+  RedundantBranchConditionCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_REDUNDANTBRANCHCONDITIONCHECK_H

diff  --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index cc5594e54aa2..781fef27c476 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -67,8 +67,11 @@ The improvements are...
 Improvements to clang-tidy
 --------------------------
 
-Changes in existing checks
-^^^^^^^^^^^^^^^^^^^^^^^^^^
+- New :doc:`bugprone-redundant-branch-condition
+  <clang-tidy/checks/bugprone-redundant-branch-condition>` check.
+
+  Finds condition variables in nested ``if`` statements that were also checked
+  in the outer ``if`` statement and were not changed.
 
 - New :doc:`cppcoreguidelines-prefer-member-initializer
   <clang-tidy/checks/cppcoreguidelines-prefer-member-initializer>` check.
@@ -76,6 +79,9 @@ Changes in existing checks
   Finds member initializations in the constructor body which can be placed into
   the initialization list instead.
 
+Changes in existing checks
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 - Improved :doc:`readability-identifier-naming
   <clang-tidy/checks/readability-identifier-naming>` check.
 

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone-redundant-branch-condition.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone-redundant-branch-condition.rst
new file mode 100644
index 000000000000..8bc97f4114ae
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone-redundant-branch-condition.rst
@@ -0,0 +1,104 @@
+.. title:: clang-tidy - bugprone-redundant-branch-condition
+
+bugprone-redundant-branch-condition
+===================================
+
+Finds condition variables in nested ``if`` statements that were also checked in
+the outer ``if`` statement and were not changed.
+
+Simple example:
+
+.. code-block:: c
+
+  bool onFire = isBurning();
+  if (onFire) {
+    if (onFire)
+      scream();
+  }
+
+Here `onFire` is checked both in the outer ``if`` and the inner ``if`` statement
+without a possible change between the two checks. The check warns for this code
+and suggests removal of the second checking of variable `onFire`.
+
+The checker also detects redundant condition checks if the condition variable
+is an operand of a logical "and" (``&&``) or a logical "or" (``||``) operator:
+
+.. code-block:: c
+
+  bool onFire = isBurning();
+  if (onFire) {
+    if (onFire && peopleInTheBuilding > 0)
+      scream();
+  }
+
+.. code-block:: c
+
+  bool onFire = isBurning();
+  if (onFire) {
+    if (onFire || isCollapsing())
+      scream();
+  }
+
+In the first case (logical "and") the suggested fix is to remove the redundant
+condition variable and keep the other side of the ``&&``. In the second case
+(logical "or") the whole ``if`` is removed similarily to the simple case on the
+top.
+
+The condition of the outer ``if`` statement may also be a logical "and" (``&&``)
+expression:
+
+.. code-block:: c
+
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    if (someOtherCondition()) {
+      if (onFire)
+        scream();
+    }
+  }
+
+The error is also detected if both the outer statement is a logical "and"
+(``&&``) and the inner statement is a logical "and" (``&&``) or "or" (``||``).
+The inner ``if`` statement does not have to be a direct descendant of the outer
+one.
+
+No error is detected if the condition variable may have been changed between the
+two checks:
+
+.. code-block:: c
+
+  bool onFire = isBurning();
+  if (onFire) {
+    tryToExtinguish(onFire);
+    if (onFire && peopleInTheBuilding > 0)
+      scream();
+  }
+
+Every possible change is considered, thus if the condition variable is not
+a local variable of the function, it is a volatile or it has an alias (pointer
+or reference) then no warning is issued.
+
+Known limitations
+^^^^^^^^^^^^^^^^^
+
+The ``else`` branch is not checked currently for negated condition variable:
+
+  bool onFire = isBurning();
+  if (onFire) {
+    scream();
+  } else {
+    if (!onFire) {
+      continueWork();
+    }
+  }
+
+The checker currently only detects redundant checking of single condition
+variables. More complex expressions are not checked:
+
+.. code-block:: c
+
+  if (peopleInTheBuilding == 1) {
+    if (peopleInTheBuilding == 1) {
+      doSomething();
+    }
+  }

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index ee065c645404..91414ee8c90f 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -70,10 +70,11 @@ Clang-Tidy Checks
    `bugprone-misplaced-widening-cast <bugprone-misplaced-widening-cast.html>`_,
    `bugprone-move-forwarding-reference <bugprone-move-forwarding-reference.html>`_, "Yes"
    `bugprone-multiple-statement-macro <bugprone-multiple-statement-macro.html>`_,
-   `bugprone-no-escape <bugprone-no-escape.html>`_, "Yes"
+   `bugprone-no-escape <bugprone-no-escape.html>`_,
    `bugprone-not-null-terminated-result <bugprone-not-null-terminated-result.html>`_, "Yes"
    `bugprone-parent-virtual-call <bugprone-parent-virtual-call.html>`_, "Yes"
    `bugprone-posix-return <bugprone-posix-return.html>`_, "Yes"
+   `bugprone-redundant-branch-condition <bugprone-redundant-branch-condition.html>`_, "Yes"
    `bugprone-reserved-identifier <bugprone-reserved-identifier.html>`_, "Yes"
    `bugprone-signed-char-misuse <bugprone-signed-char-misuse.html>`_,
    `bugprone-sizeof-container <bugprone-sizeof-container.html>`_,

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-redundant-branch-condition.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-redundant-branch-condition.cpp
new file mode 100644
index 000000000000..2bfd49a82955
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-redundant-branch-condition.cpp
@@ -0,0 +1,1190 @@
+// RUN: %check_clang_tidy %s bugprone-redundant-branch-condition %t
+
+extern unsigned peopleInTheBuilding;
+extern unsigned fireFighters;
+
+bool isBurning();
+bool isReallyBurning();
+bool isCollapsing();
+void tryToExtinguish(bool&);
+void tryPutFireOut();
+bool callTheFD();
+void scream();
+
+bool someOtherCondition();
+
+//===--- Basic Positives --------------------------------------------------===//
+
+void positive_direct() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (onFire) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: {{^\ *$}}
+      scream();
+    }
+    // CHECK-FIXES: {{^\ *$}}
+  }
+}
+
+void positive_indirect() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (someOtherCondition()) {
+      if (onFire)
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: {{^\ *$}}
+        scream();
+    }
+  }
+}
+
+void positive_direct_inner_and_lhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (onFire && peopleInTheBuilding > 0) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: if ( peopleInTheBuilding > 0) {
+      scream();
+    }
+  }
+}
+
+void positive_indirect_inner_and_lhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (someOtherCondition()) {
+      if (onFire && peopleInTheBuilding > 0) {
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: if ( peopleInTheBuilding > 0) {
+        scream();
+      }
+    }
+  }
+}
+
+void positive_direct_inner_and_rhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (peopleInTheBuilding > 0 && onFire) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: if (peopleInTheBuilding > 0 ) {
+      scream();
+    }
+  }
+}
+
+void positive_indirect_inner_and_rhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (someOtherCondition()) {
+      if (peopleInTheBuilding > 0 && onFire) {
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: if (peopleInTheBuilding > 0 ) {
+        scream();
+      }
+    }
+  }
+}
+
+void positive_direct_inner_or_lhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (onFire || isCollapsing()) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: {{^\ *$}}
+      scream();
+    }
+    // CHECK-FIXES: {{^\ *$}}
+  }
+}
+
+void positive_indirect_inner_or_lhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (someOtherCondition()) {
+      if (onFire || isCollapsing()) {
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: {{^\ *$}}
+        scream();
+      }
+      // CHECK-FIXES: {{^\ *$}}
+    }
+  }
+}
+
+void positive_direct_inner_or_rhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (isCollapsing() || onFire) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: {{^\ *$}}
+      scream();
+    }
+    // CHECK-FIXES: {{^\ *$}}
+  }
+}
+
+void positive_indirect_inner_or_rhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (someOtherCondition()) {
+      if (isCollapsing() || onFire) {
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: {{^\ *$}}
+        scream();
+      }
+      // CHECK-FIXES: {{^\ *$}}
+    }
+  }
+}
+
+void positive_direct_outer_and_lhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    if (onFire) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+    // CHECK-FIXES: {{^\ *$}}
+      scream();
+    }
+    // CHECK-FIXES: {{^\ *$}}
+  }
+}
+
+void positive_indirect_outer_and_lhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    if (someOtherCondition()) {
+      if (onFire) {
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: {{^\ *$}}
+        scream();
+      }
+      // CHECK-FIXES: {{^\ *$}}
+    }
+  }
+}
+
+void positive_direct_outer_and_lhs_inner_and_lhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    if (onFire && peopleInTheBuilding > 0) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: if ( peopleInTheBuilding > 0) {
+      scream();
+    }
+  }
+}
+
+void positive_indirect_outer_and_lhs_inner_and_lhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    if (someOtherCondition()) {
+      if (onFire && peopleInTheBuilding > 0) {
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: if ( peopleInTheBuilding > 0) {
+        scream();
+      }
+    }
+  }
+}
+
+void positive_direct_outer_and_lhs_inner_and_rhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    if (peopleInTheBuilding > 0 && onFire) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: if (peopleInTheBuilding > 0 ) {
+      scream();
+    }
+  }
+}
+
+void positive_indirect_outer_and_lhs_inner_and_rhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    if (someOtherCondition()) {
+      if (peopleInTheBuilding > 0 && onFire) {
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: if (peopleInTheBuilding > 0 ) {
+        scream();
+      }
+    }
+  }
+}
+
+void positive_direct_outer_and_lhs_inner_or_lhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    if (onFire || isCollapsing()) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: {{^\ *$}}
+      scream();
+    }
+    // CHECK-FIXES: {{^\ *$}}
+  }
+}
+
+void positive_indirect_outer_and_lhs_inner_or_lhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    if (someOtherCondition()) {
+      if (onFire || isCollapsing()) {
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: {{^\ *$}}
+        scream();
+      }
+    // CHECK-FIXES: {{^\ *$}}
+    }
+  }
+}
+
+void positive_direct_outer_and_lhs_inner_or_rhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    if (isCollapsing() || onFire) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: {{^\ *$}}
+      scream();
+    }
+    // CHECK-FIXES: {{^\ *$}}
+  }
+}
+
+void positive_indirect_outer_and_lhs_inner_or_rhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    if (someOtherCondition()) {
+      if (isCollapsing() || onFire) {
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: {{^\ *$}}
+        scream();
+      }
+      // CHECK-FIXES: {{^\ *$}}
+    }
+  }
+}
+
+void positive_direct_outer_and_rhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    if (onFire) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: {{^\ *$}}
+      scream();
+    }
+    // CHECK-FIXES: {{^\ *$}}
+  }
+}
+
+void positive_indirect_outer_and_rhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    if (someOtherCondition()) {
+      if (onFire) {
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: {{^\ *$}}
+        scream();
+      }
+      // CHECK-FIXES: {{^\ *$}}
+    }
+  }
+}
+
+void positive_direct_outer_and_rhs_inner_and_lhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    if (onFire && peopleInTheBuilding > 0) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: if ( peopleInTheBuilding > 0) {
+      scream();
+    }
+  }
+}
+
+void positive_indirect_outer_and_rhs_inner_and_lhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    if (someOtherCondition()) {
+      if (onFire && peopleInTheBuilding > 0) {
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: if ( peopleInTheBuilding > 0) {
+        scream();
+      }
+    }
+  }
+}
+
+void positive_direct_inner_outer_and_rhs_and_rhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    if (peopleInTheBuilding > 0 && onFire) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: if (peopleInTheBuilding > 0 ) {
+      scream();
+    }
+  }
+}
+
+void positive_indirect_outer_and_rhs_inner_and_rhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    if (someOtherCondition()) {
+      if (peopleInTheBuilding > 0 && onFire) {
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: if (peopleInTheBuilding > 0 ) {
+        scream();
+      }
+    }
+  }
+}
+
+void positive_direct_outer_and_rhs_inner_or_lhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    if (onFire || isCollapsing()) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: {{^\ *$}}
+      scream();
+    }
+    // CHECK-FIXES: {{^\ *$}}
+  }
+}
+
+void positive_indirect_outer_and_rhs_inner_or_lhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    if (someOtherCondition()) {
+      if (onFire || isCollapsing()) {
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: {{^\ *$}}
+        scream();
+      }
+      // CHECK-FIXES: {{^\ *$}}
+    }
+  }
+}
+
+void positive_direct_outer_and_rhs_inner_or_rhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    if (isCollapsing() || onFire) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: {{^\ *$}}
+      scream();
+    }
+    // CHECK-FIXES: {{^\ *$}}
+  }
+}
+
+void positive_indirect_outer_and_rhs_inner_or_rhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    if (someOtherCondition()) {
+      if (isCollapsing() || onFire) {
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: {{^\ *$}}
+        scream();
+      }
+      // CHECK-FIXES: {{^\ *$}}
+    }
+  }
+}
+
+//===--- Basic Negatives --------------------------------------------------===//
+
+void negative_direct() {
+  bool onFire = isBurning();
+  if (onFire) {
+    tryToExtinguish(onFire);
+    if (onFire) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+void negative_indirect() {
+  bool onFire = isBurning();
+  if (onFire) {
+    tryToExtinguish(onFire);
+    if (someOtherCondition()) {
+      if (onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_indirect2() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (someOtherCondition()) {
+      tryToExtinguish(onFire);
+      if (onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_direct_inner_and_lhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    tryToExtinguish(onFire);
+    if (onFire && peopleInTheBuilding > 0) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+void negative_indirect_inner_and_lhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    tryToExtinguish(onFire);
+    if (someOtherCondition()) {
+      if (onFire && peopleInTheBuilding > 0) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_indirect2_inner_and_lhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (someOtherCondition()) {
+      tryToExtinguish(onFire);
+      if (onFire && peopleInTheBuilding > 0) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_direct_inner_and_rhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    tryToExtinguish(onFire);
+    if (peopleInTheBuilding > 0 && onFire) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+void negative_indirect_inner_and_rhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    tryToExtinguish(onFire);
+    if (someOtherCondition()) {
+      if (peopleInTheBuilding > 0 && onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_indirect2_inner_and_rhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (someOtherCondition()) {
+      tryToExtinguish(onFire);
+      if (peopleInTheBuilding > 0 && onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_direct_inner_or_lhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    tryToExtinguish(onFire);
+    if (onFire || isCollapsing()) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+void negative_indirect_inner_or_lhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    tryToExtinguish(onFire);
+    if (someOtherCondition()) {
+      if (onFire || isCollapsing()) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_indirect2_inner_or_lhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (someOtherCondition()) {
+      tryToExtinguish(onFire);
+      if (onFire || isCollapsing()) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_direct_inner_or_rhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    tryToExtinguish(onFire);
+    if (isCollapsing() || onFire) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+void negative_indirect_inner_or_rhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    tryToExtinguish(onFire);
+    if (someOtherCondition()) {
+      if (isCollapsing() || onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_indirect2_inner_or_rhs() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (someOtherCondition()) {
+      tryToExtinguish(onFire);
+      if (isCollapsing() || onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_direct_outer_and_lhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    tryToExtinguish(onFire);
+    if (onFire) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+void negative_indirect_outer_and_lhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    tryToExtinguish(onFire);
+    if (someOtherCondition()) {
+      if (onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_indirect2_outer_and_lhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    if (someOtherCondition()) {
+      tryToExtinguish(onFire);
+      if (onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_direct_outer_and_lhs_inner_and_lhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    tryToExtinguish(onFire);
+    if (onFire && peopleInTheBuilding > 0) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+void negative_indirect_outer_and_lhs_inner_and_lhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    tryToExtinguish(onFire);
+    if (someOtherCondition()) {
+      if (onFire && peopleInTheBuilding > 0) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_indirect2_outer_and_lhs_inner_and_lhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    if (someOtherCondition()) {
+      tryToExtinguish(onFire);
+      if (onFire && peopleInTheBuilding > 0) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_direct_outer_and_lhs_inner_and_rhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    tryToExtinguish(onFire);
+    if (peopleInTheBuilding > 0 && onFire) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+void negative_indirect_outer_and_lhs_inner_and_rhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    tryToExtinguish(onFire);
+    if (someOtherCondition()) {
+      if (peopleInTheBuilding > 0 && onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_indirect2_outer_and_lhs_inner_and_rhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    if (someOtherCondition()) {
+      tryToExtinguish(onFire);
+      if (peopleInTheBuilding > 0 && onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_direct_outer_and_lhs_inner_or_lhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    tryToExtinguish(onFire);
+    if (onFire || isCollapsing()) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+void negative_indirect_outer_and_lhs_inner_or_lhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    tryToExtinguish(onFire);
+    if (someOtherCondition()) {
+      if (onFire || isCollapsing()) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_indirect2_outer_and_lhs_inner_or_lhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    if (someOtherCondition()) {
+      tryToExtinguish(onFire);
+      if (onFire || isCollapsing()) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_direct_outer_and_lhs_inner_or_rhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    tryToExtinguish(onFire);
+    if (isCollapsing() || onFire) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+void negative_indirect_outer_and_lhs_inner_or_rhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    tryToExtinguish(onFire);
+    if (someOtherCondition()) {
+      if (isCollapsing() || onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_indirect2_outer_and_lhs_inner_or_rhs() {
+  bool onFire = isBurning();
+  if (onFire && fireFighters < 10) {
+    if (someOtherCondition()) {
+      tryToExtinguish(onFire);
+      if (isCollapsing() || onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_direct_outer_and_rhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    tryToExtinguish(onFire);
+    if (onFire) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+void negative_indirect_outer_and_rhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    tryToExtinguish(onFire);
+    if (someOtherCondition()) {
+      if (onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_indirect2_outer_and_rhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    if (someOtherCondition()) {
+      tryToExtinguish(onFire);
+      if (onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_direct_outer_and_rhs_inner_and_lhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    tryToExtinguish(onFire);
+    if (onFire && peopleInTheBuilding > 0) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+void negative_indirect_outer_and_rhs_inner_and_lhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    tryToExtinguish(onFire);
+    if (someOtherCondition()) {
+      if (onFire && peopleInTheBuilding > 0) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_indirect2_outer_and_rhs_inner_and_lhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    if (someOtherCondition()) {
+      tryToExtinguish(onFire);
+      if (onFire && peopleInTheBuilding > 0) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_direct_inner_outer_and_rhs_and_rhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    tryToExtinguish(onFire);
+    if (peopleInTheBuilding > 0 && onFire) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+void negative_indirect_outer_and_rhs_inner_and_rhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    tryToExtinguish(onFire);
+    if (someOtherCondition()) {
+      if (peopleInTheBuilding > 0 && onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_indirect2_outer_and_rhs_inner_and_rhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    if (someOtherCondition()) {
+      tryToExtinguish(onFire);
+      if (peopleInTheBuilding > 0 && onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_direct_outer_and_rhs_inner_or_lhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    tryToExtinguish(onFire);
+    if (onFire || isCollapsing()) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+void negative_indirect_outer_and_rhs_inner_or_lhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    tryToExtinguish(onFire);
+    if (someOtherCondition()) {
+      if (onFire || isCollapsing()) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_indirect2_outer_and_rhs_inner_or_lhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    if (someOtherCondition()) {
+      tryToExtinguish(onFire);
+      if (onFire || isCollapsing()) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_direct_outer_and_rhs_inner_or_rhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    tryToExtinguish(onFire);
+    if (isCollapsing() || onFire) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+void negative_indirect_outer_and_rhs_inner_or_rhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    tryToExtinguish(onFire);
+    if (someOtherCondition()) {
+      if (isCollapsing() || onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_indirect2_outer_and_rhs_inner_or_rhs() {
+  bool onFire = isBurning();
+  if (fireFighters < 10 && onFire) {
+    if (someOtherCondition()) {
+      tryToExtinguish(onFire);
+      if (isCollapsing() || onFire) {
+        // NO-MESSAGE: fire may have been extinguished
+        scream();
+      }
+    }
+  }
+}
+
+void negative_reassigned() {
+  bool onFire = isBurning();
+  if (onFire) {
+    onFire = isReallyBurning();
+    if (onFire) {
+      // NO-MESSAGE: it was a false alarm then
+      scream();
+    }
+  }
+}
+
+//===--- Special Positives ------------------------------------------------===//
+
+// Condition variable mutated in or after the inner loop
+
+void positive_direct_mutated_after_inner() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (onFire) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: {{^\ *$}}
+      scream();
+    }
+    // CHECK-FIXES: {{^\ *$}}
+    tryToExtinguish(onFire);
+  }
+}
+
+void positive_indirect_mutated_after_inner() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (someOtherCondition()) {
+      if (onFire) {
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: {{^\ *$}}
+        scream();
+      }
+      // CHECK-FIXES: {{^\ *$}}
+    }
+    tryToExtinguish(onFire);
+  }
+}
+
+void positive_indirect2_mutated_after_inner() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (someOtherCondition()) {
+      if (onFire) {
+        // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+        // CHECK-FIXES: {{^\ *$}}
+        scream();
+      }
+      // CHECK-FIXES: {{^\ *$}}
+      tryToExtinguish(onFire);
+    }
+  }
+}
+
+void positive_mutated_in_inner() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (onFire) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: {{^\ *$}}
+      tryToExtinguish(onFire);
+      scream();
+    }
+    // CHECK-FIXES: {{^\ *$}}
+  }
+}
+
+void positive_or_lhs_with_side_effect() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (callTheFD() || onFire) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: callTheFD() ;
+      scream();
+    }
+    // CHECK-FIXES: {{^\ *$}}
+  }
+}
+
+void positive_or_rhs_with_side_effect() {
+  bool onFire = isBurning();
+  if (onFire) {
+    if (onFire || callTheFD()) {
+      // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHECK-FIXES: callTheFD();
+      scream();
+    }
+    // CHECK-FIXES: {{^\ *$}}
+  }
+}
+
+// GNU Expression Statements
+
+void doSomething();
+
+void positive_gnu_expression_statement() {
+  bool onFire = isBurning();
+  if (({ doSomething(); onFire; })) {
+    if (({ doSomething(); onFire; })) {
+      // FIXME: Handle GNU epxression statements
+      // CHECK-MESSAGES-NOT: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHCK-FIXES-NOT: doSomething();
+      scream();
+    }
+  }
+}
+
+// Comma after Condition
+
+void positive_comma_after_condition() {
+  bool onFire = isBurning();
+  if (doSomething(), onFire) {
+    if (doSomething(), onFire) {
+      // FIXME: Handle comma operator
+      // CHECK-MESSAGES-NOT: :[[@LINE-1]]:5: warning: redundant condition 'onFire' [bugprone-redundant-branch-condition]
+      // CHCK-FIXES-NOT: doSomething();
+      scream();
+    }
+  }
+}
+
+//===--- Special Negatives ------------------------------------------------===//
+
+// Aliasing
+
+void negative_mutated_by_ptr() {
+  bool onFire = isBurning();
+  bool *firePtr = &onFire;
+  if (onFire) {
+    tryToExtinguish(*firePtr);
+    if (onFire) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+void negative_mutated_by_ref() {
+  bool onFire = isBurning();
+  bool &fireRef = onFire;
+  if (onFire) {
+    tryToExtinguish(fireRef);
+    if (onFire) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+// Volatile
+
+void negatvie_volatile() {
+  bool volatile onFire = isBurning();
+  if (onFire) {
+    if (onFire) {
+      // NO-MESSAGE: maybe some other thread extinguished the fire
+      scream();
+    }
+  }
+}
+
+void negative_else_branch(bool isHot) {
+  bool onFire = isBurning();
+  if (onFire) {
+    tryPutFireOut();
+  } else {
+    if (isHot && onFire) {
+      // NO-MESSAGE: new check is in the `else` branch
+      // FIXME: handle `else` branches and negated conditions
+      scream();
+    }
+  }
+}
+
+// GNU Expression Statements
+
+void negative_gnu_expression_statement() {
+  bool onFire = isBurning();
+  if (({ doSomething(); onFire; })) {
+    tryToExtinguish(onFire);
+    if (({ doSomething(); onFire; })) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+// Comma after Condition
+
+void negative_comma_after_condition() {
+  bool onFire = isBurning();
+  if (doSomething(), onFire) {
+    tryToExtinguish(onFire);
+    if (doSomething(), onFire) {
+      // NO-MESSAGE: fire may have been extinguished
+      scream();
+    }
+  }
+}
+
+//===--- Unhandled Cases --------------------------------------------------===//
+
+void negated_in_else() {
+  bool onFire = isBurning();
+  if (onFire) {
+    scream();
+  } else {
+    if (!onFire) {
+      doSomething();
+    }
+  }
+}
+
+void equality() {
+  if (peopleInTheBuilding == 1) {
+    if (peopleInTheBuilding == 1) {
+      doSomething();
+    }
+  }
+}
+
+void relational_operator() {
+  if (peopleInTheBuilding > 2) {
+    if (peopleInTheBuilding > 1) {
+      doSomething();
+    }
+  }
+}
+
+void relational_operator_reversed() {
+  if (peopleInTheBuilding > 1) {
+    if (1 < peopleInTheBuilding) {
+      doSomething();
+    }
+  }
+}


        


More information about the cfe-commits mailing list