Index: clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp =================================================================== --- clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp +++ clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp @@ -10,8 +10,10 @@ #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h" +#include "../utils/Aliasing.h" using namespace clang::ast_matchers; +using clang::tidy::utils::hasPtrOrReferenceInFunc; namespace clang { namespace tidy { @@ -24,54 +26,6 @@ callExpr(Internal, callee(functionDecl(isNoReturn()))))); } -/// Return whether `S` is a reference to the declaration of `Var`. -static bool isAccessForVar(const Stmt *S, const VarDecl *Var) { - if (const auto *DRE = dyn_cast(S)) - return DRE->getDecl() == Var; - - return false; -} - -/// Return whether `Var` has a pointer or reference in `S`. -static bool isPtrOrReferenceForVar(const Stmt *S, const VarDecl *Var) { - if (const auto *DS = dyn_cast(S)) { - for (const Decl *D : DS->getDeclGroup()) { - if (const auto *LeftVar = dyn_cast(D)) { - if (LeftVar->hasInit() && LeftVar->getType()->isReferenceType()) { - return isAccessForVar(LeftVar->getInit(), Var); - } - } - } - } else if (const auto *UnOp = dyn_cast(S)) { - if (UnOp->getOpcode() == UO_AddrOf) - return isAccessForVar(UnOp->getSubExpr(), Var); - } - - return false; -} - -/// Return whether `Var` has a pointer or reference in `S`. -static bool hasPtrOrReferenceInStmt(const Stmt *S, const VarDecl *Var) { - if (isPtrOrReferenceForVar(S, Var)) - return true; - - for (const Stmt *Child : S->children()) { - if (!Child) - continue; - - if (hasPtrOrReferenceInStmt(Child, Var)) - return true; - } - - return false; -} - -/// Return whether `Var` has a pointer or reference in `Func`. -static bool hasPtrOrReferenceInFunc(const FunctionDecl *Func, - const VarDecl *Var) { - return hasPtrOrReferenceInStmt(Func->getBody(), Var); -} - /// Return whether `Var` was changed in `LoopStmt`. static bool isChanged(const Stmt *LoopStmt, const VarDecl *Var, ASTContext *Context) { Index: clang-tools-extra/clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tools-extra/clang-tidy/misc/CMakeLists.txt +++ clang-tools-extra/clang-tidy/misc/CMakeLists.txt @@ -11,6 +11,7 @@ NoRecursionCheck.cpp NonCopyableObjects.cpp NonPrivateMemberVariablesInClassesCheck.cpp + RedundantConditionCheck.cpp RedundantExpressionCheck.cpp StaticAssertCheck.cpp ThrowByValueCatchByReferenceCheck.cpp Index: clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp +++ clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp @@ -15,6 +15,7 @@ #include "NoRecursionCheck.h" #include "NonCopyableObjects.h" #include "NonPrivateMemberVariablesInClassesCheck.h" +#include "RedundantConditionCheck.h" #include "RedundantExpressionCheck.h" #include "StaticAssertCheck.h" #include "ThrowByValueCatchByReferenceCheck.h" @@ -41,6 +42,8 @@ "misc-non-copyable-objects"); CheckFactories.registerCheck( "misc-non-private-member-variables-in-classes"); + CheckFactories.registerCheck( + "misc-redundant-condition"); CheckFactories.registerCheck( "misc-redundant-expression"); CheckFactories.registerCheck("misc-static-assert"); Index: clang-tools-extra/clang-tidy/misc/RedundantConditionCheck.h =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/misc/RedundantConditionCheck.h @@ -0,0 +1,35 @@ +//===--- RedundantConditionCheck.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_MISC_REDUNDANTCONDITIONCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_REDUNDANTCONDITIONCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace misc { + +/// 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/misc-redundant-condition.html +class RedundantConditionCheck : public ClangTidyCheck { +public: + RedundantConditionCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_REDUNDANTCONDITIONCHECK_H Index: clang-tools-extra/clang-tidy/misc/RedundantConditionCheck.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/misc/RedundantConditionCheck.cpp @@ -0,0 +1,127 @@ +//===--- RedundantConditionCheck.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 "RedundantConditionCheck.h" +#include "../utils/Aliasing.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h" + +using namespace clang::ast_matchers; +using clang::tidy::utils::hasPtrOrReferenceInFunc; + +namespace clang { +namespace tidy { +namespace misc { + +/// 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(); + if (const Stmt *MutS = MutAn.findMutation(Var)) { + if (SM.isBeforeInTranslationUnit(MutS->getEndLoc(), NextS->getBeginLoc())) + return true; + } + + return false; +} + +void RedundantConditionCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + ifStmt(hasCondition(anyOf( + declRefExpr(hasDeclaration(varDecl().bind("cond_var"))), + binaryOperator(hasOperatorName("&&"), + hasEitherOperand(declRefExpr(hasDeclaration( + varDecl().bind("cond_var"))))))), + hasDescendant( + ifStmt(hasCondition(anyOf( + declRefExpr(hasDeclaration( + varDecl(equalsBoundNode("cond_var")))), + binaryOperator( + hasAnyOperatorName("&&", "||"), + hasEitherOperand(declRefExpr(hasDeclaration( + varDecl(equalsBoundNode("cond_var"))))))))) + .bind("inner_if")), + forFunction(functionDecl().bind("func"))) + .bind("outer_if"), + this); +} + +void RedundantConditionCheck::check(const MatchFinder::MatchResult &Result) { + const auto *OuterIf = Result.Nodes.getNodeAs("outer_if"); + const auto *InnerIf = Result.Nodes.getNodeAs("inner_if"); + const auto *CondVar = Result.Nodes.getNodeAs("cond_var"); + const auto *Func = Result.Nodes.getNodeAs("func"); + + // Non-local variables may be changed by any call. + if (!CondVar->isLocalVarDeclOrParm()) + return; + + // Volatile variables may be changed by another thread. + if (CondVar->getType().isVolatileQualified()) + return; + + // Class instances may be tricky, so restrict ourselves to integers. + if (!CondVar->getType().getTypePtr()->isIntegerType()) + return; + + // If the variable has an alias then it can be changed by that alias as well + // FIXME: Track pointers and references. + if (hasPtrOrReferenceInFunc(Func, CondVar)) + return; + + if (isChangedBefore(OuterIf, 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`. + if (isa(InnerIf->getCond()->IgnoreParenImpCasts()) || + (isa(InnerIf->getCond()) && + cast(InnerIf->getCond())->getOpcode() == BO_LOr)) { + SourceLocation IfBegin = InnerIf->getBeginLoc(); + const Stmt *Body = InnerIf->getThen(); + + // For comound statements also remove the right brace at the end. + if (isa(Body)) { + SourceLocation IfEnd = Body->getBeginLoc().getLocWithOffset(1); + Diag << FixItHint::CreateRemoval( + CharSourceRange::getCharRange(IfBegin, IfEnd)) + << FixItHint::CreateRemoval(CharSourceRange::getCharRange( + Body->getEndLoc(), Body->getEndLoc().getLocWithOffset(1))); + } else { + SourceLocation IfEnd = Body->getBeginLoc(); + Diag << FixItHint::CreateRemoval( + CharSourceRange::getCharRange(IfBegin, IfEnd)); + } + + // For "and" binary operations we remove the "and" operation with the + // condition variable from the inner if. + } else { + const auto *CondOp = cast(InnerIf->getCond()); + if (isa(CondOp->getLHS()->IgnoreParenImpCasts()) && + cast(CondOp->getLHS()->IgnoreParenImpCasts())->getDecl() == + CondVar) { + Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange( + CondOp->getLHS()->getBeginLoc(), CondOp->getRHS()->getBeginLoc())); + } else { + // For some reason the end location of identifiers is identical to their + // begin location so add their length instead. + Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange( + CondOp->getLHS()->getEndLoc().getLocWithOffset(1), + CondOp->getRHS()->getBeginLoc().getLocWithOffset( + CondVar->getIdentifier()->getLength()))); + } + } +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: clang-tools-extra/clang-tidy/utils/Aliasing.h =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/utils/Aliasing.h @@ -0,0 +1,25 @@ +//===------------- Aliasing.h - 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_ALIASING_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_ALIASING_H + +#include "clang/AST/Decl.h" + +namespace clang { +namespace tidy { +namespace utils { + +/// Returns whether a ``Var`` has a pointer or reference in ``Func`` +bool hasPtrOrReferenceInFunc(const FunctionDecl *Func, const VarDecl *Var); + +} // namespace utils +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_ALIASING_H Index: clang-tools-extra/clang-tidy/utils/Aliasing.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/utils/Aliasing.cpp @@ -0,0 +1,66 @@ +//===------------- Aliasing.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 "Aliasing.h" + +#include "clang/AST/Expr.h" + +namespace clang { +namespace tidy { +namespace utils { + +/// Return whether `S` is a reference to the declaration of `Var`. +static bool isAccessForVar(const Stmt *S, const VarDecl *Var) { + if (const auto *DRE = dyn_cast(S)) + return DRE->getDecl() == Var; + + return false; +} + +/// Return whether `Var` has a pointer or reference in `S`. +static bool isPtrOrReferenceForVar(const Stmt *S, const VarDecl *Var) { + if (const auto *DS = dyn_cast(S)) { + for (const Decl *D : DS->getDeclGroup()) { + if (const auto *LeftVar = dyn_cast(D)) { + if (LeftVar->hasInit() && LeftVar->getType()->isReferenceType()) { + return isAccessForVar(LeftVar->getInit(), Var); + } + } + } + } else if (const auto *UnOp = dyn_cast(S)) { + if (UnOp->getOpcode() == UO_AddrOf) + return isAccessForVar(UnOp->getSubExpr(), Var); + } + + return false; +} + +/// Return whether `Var` has a pointer or reference in `S`. +static bool hasPtrOrReferenceInStmt(const Stmt *S, const VarDecl *Var) { + if (isPtrOrReferenceForVar(S, Var)) + return true; + + for (const Stmt *Child : S->children()) { + if (!Child) + continue; + + if (hasPtrOrReferenceInStmt(Child, Var)) + return true; + } + + return false; +} + +/// Return whether `Var` has a pointer or reference in `Func`. +bool hasPtrOrReferenceInFunc(const FunctionDecl *Func, const VarDecl *Var) { + return hasPtrOrReferenceInStmt(Func->getBody(), Var); +} + +} // namespace utils +} // namespace tidy +} // namespace clang Index: clang-tools-extra/clang-tidy/utils/CMakeLists.txt =================================================================== --- clang-tools-extra/clang-tidy/utils/CMakeLists.txt +++ clang-tools-extra/clang-tidy/utils/CMakeLists.txt @@ -4,6 +4,7 @@ ) add_clang_library(clangTidyUtils + Aliasing.cpp ASTUtils.cpp DeclRefExprUtils.cpp ExceptionAnalyzer.cpp Index: clang-tools-extra/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/docs/ReleaseNotes.rst +++ clang-tools-extra/docs/ReleaseNotes.rst @@ -75,6 +75,12 @@ New checks ^^^^^^^^^^ +- New :doc:`misc-redundant-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-avoid-non-const-global-variables ` check. Finds non-const global variables as described in check I.2 of C++ Core Index: clang-tools-extra/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -132,6 +132,7 @@ `clang-analyzer-valist.Uninitialized `_, `clang-analyzer-valist.Unterminated `_, `cppcoreguidelines-avoid-goto `_, + `cppcoreguidelines-avoid-non-const-global-variables `_, `cppcoreguidelines-init-variables `_, "Yes" `cppcoreguidelines-interfaces-global-init `_, `cppcoreguidelines-macro-usage `_, @@ -188,15 +189,16 @@ `llvm-prefer-isa-or-dyn-cast-in-conditionals `_, "Yes" `llvm-prefer-register-over-unsigned `_, "Yes" `llvm-twine-local `_, "Yes" - `llvmlibc-callee-namespace `_, + `llvmlibc-callee-namespace `_, `llvmlibc-implementation-in-namespace `_, - `llvmlibc-restrict-system-libc-headers `_, "Yes" + `llvmlibc-restrict-system-libc-headers `_, `misc-definitions-in-headers `_, "Yes" `misc-misplaced-const `_, `misc-new-delete-overloads `_, `misc-no-recursion `_, `misc-non-copyable-objects `_, `misc-non-private-member-variables-in-classes `_, + `misc-redundant-condition `_, "Yes" `misc-redundant-expression `_, "Yes" `misc-static-assert `_, "Yes" `misc-throw-by-value-catch-by-reference `_, @@ -211,7 +213,7 @@ `modernize-deprecated-headers `_, "Yes" `modernize-deprecated-ios-base-aliases `_, "Yes" `modernize-loop-convert `_, "Yes" - `modernize-make-shared `_, "Yes" + `modernize-make-shared `_, `modernize-make-unique `_, "Yes" `modernize-pass-by-value `_, "Yes" `modernize-raw-string-literal `_, "Yes" @@ -241,7 +243,7 @@ `objc-dealloc-in-category `_, `objc-forbidden-subclassing `_, `objc-missing-hash `_, - `objc-nsinvocation-argument-lifetime `_, "Yes" + `objc-nsinvocation-argument-lifetime `_, `objc-property-declaration `_, "Yes" `objc-super-self `_, "Yes" `openmp-exception-escape `_, @@ -387,7 +389,6 @@ `clang-analyzer-unix.cstring.NullArg `_, `Clang Static Analyzer `_, `cppcoreguidelines-avoid-c-arrays `_, `modernize-avoid-c-arrays `_, `cppcoreguidelines-avoid-magic-numbers `_, `readability-magic-numbers `_, - `cppcoreguidelines-avoid-non-const-global-variables `_, , , "" `cppcoreguidelines-c-copy-assignment-signature `_, `misc-unconventional-assign-operator `_, `cppcoreguidelines-explicit-virtual-functions `_, `modernize-use-override `_, "Yes" `cppcoreguidelines-non-private-member-variables-in-classes `_, `misc-non-private-member-variables-in-classes `_, @@ -407,7 +408,7 @@ `hicpp-new-delete-operators `_, `misc-new-delete-overloads `_, `hicpp-no-array-decay `_, `cppcoreguidelines-pro-bounds-array-to-pointer-decay `_, `hicpp-no-malloc `_, `cppcoreguidelines-no-malloc `_, - `hicpp-noexcept-move `_, `performance-noexcept-move-constructor `_, + `hicpp-noexcept-move `_, `performance-noexcept-move-constructor `_, "Yes" `hicpp-special-member-functions `_, `cppcoreguidelines-special-member-functions `_, `hicpp-static-assert `_, `misc-static-assert `_, "Yes" `hicpp-undelegated-constructor `_, `bugprone-undelegated-constructor `_, @@ -421,4 +422,3 @@ `hicpp-use-override `_, `modernize-use-override `_, "Yes" `hicpp-vararg `_, `cppcoreguidelines-pro-type-vararg `_, `llvm-qualified-auto `_, `readability-qualified-auto `_, "Yes" - Index: clang-tools-extra/docs/clang-tidy/checks/misc-redundant-condition.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/misc-redundant-condition.rst @@ -0,0 +1,80 @@ +.. title:: clang-tidy - misc-redundant-condition + +misc-redundant-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. + Index: clang-tools-extra/test/clang-tidy/checkers/misc-redundant-condition.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/misc-redundant-condition.cpp @@ -0,0 +1,1058 @@ +// RUN: %check_clang_tidy %s misc-redundant-condition %t + +extern unsigned peopleInTheBuilding; +extern unsigned fireFighters; + +bool isBurning(); +bool isReallyBurning(); +bool isCollapsing(); +void tryToExtinguish(bool&); +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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-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' [misc-redundant-condition] + // CHECK-FIXES: {{^\ *$}} + tryToExtinguish(onFire); + scream(); + } + // CHECK-FIXES: {{^\ *$}} + } +} + +//===--- 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() { + volatile bool onFire = isBurning(); + if (onFire) { + if (onFire) { + // NO-MESSAGE: maybe some other thread extinguished the fire + scream(); + } + } +}