[clang-tools-extra] r360779 - [clang-tidy] new check: bugprone-branch-clone
Kristof Umann via cfe-commits
cfe-commits at lists.llvm.org
Wed May 15 08:06:25 PDT 2019
Author: szelethus
Date: Wed May 15 08:06:25 2019
New Revision: 360779
URL: http://llvm.org/viewvc/llvm-project?rev=360779&view=rev
Log:
[clang-tidy] new check: bugprone-branch-clone
Implement a check for detecting if/else if/else chains where two or more
branches are Type I clones of each other (that is, they contain identical code)
and for detecting switch statements where two or more consecutive branches are
Type I clones of each other.
Patch by DonĂ¡t Nagy!
Differential Revision: https://reviews.llvm.org/D54757
Added:
clang-tools-extra/trunk/clang-tidy/bugprone/BranchCloneCheck.cpp
clang-tools-extra/trunk/clang-tidy/bugprone/BranchCloneCheck.h
clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-branch-clone.rst
clang-tools-extra/trunk/test/clang-tidy/bugprone-branch-clone.cpp
Modified:
clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp
clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt
clang-tools-extra/trunk/docs/ReleaseNotes.rst
clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst
Added: clang-tools-extra/trunk/clang-tidy/bugprone/BranchCloneCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/bugprone/BranchCloneCheck.cpp?rev=360779&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/bugprone/BranchCloneCheck.cpp (added)
+++ clang-tools-extra/trunk/clang-tidy/bugprone/BranchCloneCheck.cpp Wed May 15 08:06:25 2019
@@ -0,0 +1,226 @@
+//===--- BranchCloneCheck.cpp - clang-tidy --------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BranchCloneCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Analysis/CloneDetection.h"
+#include "llvm/Support/Casting.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+
+/// Returns true when the statements are Type I clones of each other.
+static bool areStatementsIdentical(const Stmt *LHS, const Stmt *RHS,
+ const ASTContext &Context) {
+ llvm::FoldingSetNodeID DataLHS, DataRHS;
+ LHS->Profile(DataLHS, Context, false);
+ RHS->Profile(DataRHS, Context, false);
+ return (DataLHS == DataRHS);
+}
+
+namespace {
+/// A branch in a switch may consist of several statements; while a branch in
+/// an if/else if/else chain is one statement (which may be a CompoundStmt).
+using SwitchBranch = llvm::SmallVector<const Stmt *, 2>;
+} // anonymous namespace
+
+/// Determines if the bodies of two branches in a switch statements are Type I
+/// clones of each other. This function only examines the body of the branch
+/// and ignores the `case X:` or `default:` at the start of the branch.
+static bool areSwitchBranchesIdentical(const SwitchBranch LHS,
+ const SwitchBranch RHS,
+ const ASTContext &Context) {
+ if (LHS.size() != RHS.size())
+ return false;
+
+ for (size_t i = 0, Size = LHS.size(); i < Size; i++) {
+ // NOTE: We strip goto labels and annotations in addition to stripping
+ // the `case X:` or `default:` labels, but it is very unlikely that this
+ // would casue false positives in real-world code.
+ if (!areStatementsIdentical(LHS[i]->stripLabelLikeStatements(),
+ RHS[i]->stripLabelLikeStatements(), Context)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+void BranchCloneCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ ifStmt(stmt().bind("if"),
+ hasParent(stmt(unless(ifStmt(hasElse(equalsBoundNode("if")))))),
+ hasElse(stmt().bind("else"))),
+ this);
+ Finder->addMatcher(switchStmt().bind("switch"), this);
+ Finder->addMatcher(conditionalOperator().bind("condOp"), this);
+}
+
+void BranchCloneCheck::check(const MatchFinder::MatchResult &Result) {
+ const ASTContext &Context = *Result.Context;
+
+ if (const auto *IS = Result.Nodes.getNodeAs<IfStmt>("if")) {
+ const Stmt *Then = IS->getThen();
+ assert(Then && "An IfStmt must have a `then` branch!");
+
+ const Stmt *Else = Result.Nodes.getNodeAs<Stmt>("else");
+ assert(Else && "We only look for `if` statements with an `else` branch!");
+
+ if (!isa<IfStmt>(Else)) {
+ // Just a simple if with no `else if` branch.
+ if (areStatementsIdentical(Then->IgnoreContainers(),
+ Else->IgnoreContainers(), Context)) {
+ diag(IS->getBeginLoc(), "if with identical then and else branches");
+ diag(IS->getElseLoc(), "else branch starts here", DiagnosticIDs::Note);
+ }
+ return;
+ }
+
+ // This is the complicated case when we start an if/else if/else chain.
+ // To find all the duplicates, we collect all the branches into a vector.
+ llvm::SmallVector<const Stmt *, 4> Branches;
+ const IfStmt *Cur = IS;
+ while (true) {
+ // Store the `then` branch.
+ Branches.push_back(Cur->getThen());
+
+ Else = Cur->getElse();
+ // The chain ends if there is no `else` branch.
+ if (!Else)
+ break;
+
+ // Check if there is another `else if`...
+ Cur = dyn_cast<IfStmt>(Else);
+ if (!Cur) {
+ // ...this is just a plain `else` branch at the end of the chain.
+ Branches.push_back(Else);
+ break;
+ }
+ }
+
+ size_t N = Branches.size();
+ llvm::BitVector KnownAsClone(N);
+
+ for (size_t i = 0; i + 1 < N; i++) {
+ // We have already seen Branches[i] as a clone of an earlier branch.
+ if (KnownAsClone[i])
+ continue;
+
+ int NumCopies = 1;
+
+ for (size_t j = i + 1; j < N; j++) {
+ if (KnownAsClone[j] ||
+ !areStatementsIdentical(Branches[i]->IgnoreContainers(),
+ Branches[j]->IgnoreContainers(), Context))
+ continue;
+
+ NumCopies++;
+ KnownAsClone[j] = true;
+
+ if (NumCopies == 2) {
+ // We report the first occurence only when we find the second one.
+ diag(Branches[i]->getBeginLoc(),
+ "repeated branch in conditional chain");
+ diag(Lexer::getLocForEndOfToken(Branches[i]->getEndLoc(), 0,
+ *Result.SourceManager, getLangOpts()),
+ "end of the original", DiagnosticIDs::Note);
+ }
+
+ diag(Branches[j]->getBeginLoc(), "clone %0 starts here",
+ DiagnosticIDs::Note)
+ << (NumCopies - 1);
+ }
+ }
+ return;
+ }
+
+ if (const auto *CO = Result.Nodes.getNodeAs<ConditionalOperator>("condOp")) {
+ // We do not try to detect chains of ?: operators.
+ if (areStatementsIdentical(CO->getTrueExpr(), CO->getFalseExpr(), Context))
+ diag(CO->getQuestionLoc(),
+ "conditional operator with identical true and false expressions");
+
+ return;
+ }
+
+ if (const auto *SS = Result.Nodes.getNodeAs<SwitchStmt>("switch")) {
+ const CompoundStmt *Body = dyn_cast_or_null<CompoundStmt>(SS->getBody());
+
+ // Code like
+ // switch (x) case 0: case 1: foobar();
+ // is legal and calls foobar() if and only if x is either 0 or 1;
+ // but we do not try to distinguish branches in such code.
+ if (!Body)
+ return;
+
+ // We will first collect the branches of the switch statements. For the
+ // sake of simplicity we say that branches are delimited by the SwitchCase
+ // (`case:` or `default:`) children of Body; that is, we ignore `case:` or
+ // `default:` labels embedded inside other statements and we do not follow
+ // the effects of `break` and other manipulation of the control-flow.
+ llvm::SmallVector<SwitchBranch, 4> Branches;
+ for (const Stmt *S : Body->body()) {
+ // If this is a `case` or `default`, we start a new, empty branch.
+ if (isa<SwitchCase>(S))
+ Branches.emplace_back();
+
+ // There may be code before the first branch (which can be dead code
+ // and can be code reached either through goto or through case labels
+ // that are embedded inside e.g. inner compound statements); we do not
+ // store those statements in branches.
+ if (!Branches.empty())
+ Branches.back().push_back(S);
+ }
+
+ auto End = Branches.end();
+ auto BeginCurrent = Branches.begin();
+ while (BeginCurrent < End) {
+ auto EndCurrent = BeginCurrent + 1;
+ while (EndCurrent < End &&
+ areSwitchBranchesIdentical(*BeginCurrent, *EndCurrent, Context)) {
+ ++EndCurrent;
+ }
+ // At this point the iterator range {BeginCurrent, EndCurrent} contains a
+ // complete family of consecutive identical branches.
+ if (EndCurrent > BeginCurrent + 1) {
+ diag(BeginCurrent->front()->getBeginLoc(),
+ "switch has %0 consecutive identical branches")
+ << static_cast<int>(std::distance(BeginCurrent, EndCurrent));
+
+ SourceLocation EndLoc = (EndCurrent - 1)->back()->getEndLoc();
+ // If the case statement is generated from a macro, it's SourceLocation
+ // may be invalid, resuling in an assertation failure down the line.
+ // While not optimal, try the begin location in this case, it's still
+ // better then nothing.
+ if (EndLoc.isInvalid())
+ EndLoc = (EndCurrent - 1)->back()->getBeginLoc();
+
+ if (EndLoc.isMacroID())
+ EndLoc = Context.getSourceManager().getExpansionLoc(EndLoc);
+
+ diag(Lexer::getLocForEndOfToken(EndLoc, 0, *Result.SourceManager,
+ getLangOpts()),
+ "last of these clones ends here", DiagnosticIDs::Note);
+ }
+ BeginCurrent = EndCurrent;
+ }
+ return;
+ }
+
+ llvm_unreachable("No if statement and no switch statement.");
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
Added: clang-tools-extra/trunk/clang-tidy/bugprone/BranchCloneCheck.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/bugprone/BranchCloneCheck.h?rev=360779&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/bugprone/BranchCloneCheck.h (added)
+++ clang-tools-extra/trunk/clang-tidy/bugprone/BranchCloneCheck.h Wed May 15 08:06:25 2019
@@ -0,0 +1,39 @@
+//===--- BranchCloneCheck.h - clang-tidy ------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_BRANCHCLONECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_BRANCHCLONECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// A check for detecting if/else if/else chains where two or more branches are
+/// Type I clones of each other (that is, they contain identical code), for
+/// detecting switch statements where two or more consecutive branches are
+/// Type I clones of each other, and for detecting conditional operators where
+/// the true and false expressions are Type I clones of each other.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-branch-clone.html
+class BranchCloneCheck : public ClangTidyCheck {
+public:
+ BranchCloneCheck(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_BRANCHCLONECHECK_H
Modified: clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp?rev=360779&r1=360778&r2=360779&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp (original)
+++ clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp Wed May 15 08:06:25 2019
@@ -13,6 +13,7 @@
#include "ArgumentCommentCheck.h"
#include "AssertSideEffectCheck.h"
#include "BoolPointerImplicitConversionCheck.h"
+#include "BranchCloneCheck.h"
#include "CopyConstructorInitCheck.h"
#include "DanglingHandleCheck.h"
#include "ExceptionEscapeCheck.h"
@@ -65,6 +66,8 @@ public:
"bugprone-assert-side-effect");
CheckFactories.registerCheck<BoolPointerImplicitConversionCheck>(
"bugprone-bool-pointer-implicit-conversion");
+ CheckFactories.registerCheck<BranchCloneCheck>(
+ "bugprone-branch-clone");
CheckFactories.registerCheck<CopyConstructorInitCheck>(
"bugprone-copy-constructor-init");
CheckFactories.registerCheck<DanglingHandleCheck>(
Modified: clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt?rev=360779&r1=360778&r2=360779&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt Wed May 15 08:06:25 2019
@@ -4,6 +4,7 @@ add_clang_library(clangTidyBugproneModul
ArgumentCommentCheck.cpp
AssertSideEffectCheck.cpp
BoolPointerImplicitConversionCheck.cpp
+ BranchCloneCheck.cpp
BugproneTidyModule.cpp
CopyConstructorInitCheck.cpp
DanglingHandleCheck.cpp
Modified: clang-tools-extra/trunk/docs/ReleaseNotes.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/ReleaseNotes.rst?rev=360779&r1=360778&r2=360779&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/ReleaseNotes.rst (original)
+++ clang-tools-extra/trunk/docs/ReleaseNotes.rst Wed May 15 08:06:25 2019
@@ -108,6 +108,13 @@ Improvements to clang-tidy
against self-assignment either by checking self-assignment explicitly or
using the copy-and-swap or the copy-and-move method.
+- New :doc:`bugprone-branch-clone
+ <clang-tidy/checks/bugprone-branch-clone>` check.
+
+ Checks for repeated branches in ``if/else if/else`` chains, consecutive
+ repeated branches in ``switch`` statements and indentical true and false
+ branches in conditional operators.
+
- New :doc:`google-readability-avoid-underscore-in-googletest-name
<clang-tidy/checks/google-readability-avoid-underscore-in-googletest-name>`
check.
Added: clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-branch-clone.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-branch-clone.rst?rev=360779&view=auto
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-branch-clone.rst (added)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-branch-clone.rst Wed May 15 08:06:25 2019
@@ -0,0 +1,90 @@
+.. title:: clang-tidy - bugprone-branch-clone
+
+bugprone-branch-clone
+=====================
+
+Checks for repeated branches in ``if/else if/else`` chains, consecutive
+repeated branches in ``switch`` statements and indentical true and false
+branches in conditional operators.
+
+.. code-block:: c++
+
+ if (test_value(x)) {
+ y++;
+ do_something(x, y);
+ } else {
+ y++;
+ do_something(x, y);
+ }
+
+In this simple example (which could arise e.g. as a copy-paste error) the
+``then`` and ``else`` branches are identical and the code is equivalent the
+following shorter and cleaner code:
+
+.. code-block:: c++
+
+ test_value(x); // can be omitted unless it has side effects
+ y++;
+ do_something(x, y);
+
+
+If this is the inteded behavior, then there is no reason to use a conditional
+statement; otherwise the issue can be solved by fixing the branch that is
+handled incorrectly.
+
+The check also detects repeated branches in longer ``if/else if/else`` chains
+where it would be even harder to notice the problem.
+
+In ``switch`` statements the check only reports repeated branches when they are
+consecutive, because it is relatively common that the ``case:`` labels have
+some natural ordering and rearranging them would decrease the readability of
+the code. For example:
+
+.. code-block:: c++
+
+ switch (ch) {
+ case 'a':
+ return 10;
+ case 'A':
+ return 10;
+ case 'b':
+ return 11;
+ case 'B':
+ return 11;
+ default:
+ return 10;
+ }
+
+Here the check reports that the ``'a'`` and ``'A'`` branches are identical
+(and that the ``'b'`` and ``'B'`` branches are also identical), but does not
+report that the ``default:`` branch is also idenical to the first two branches.
+If this is indeed the correct behavior, then it could be implemented as:
+
+.. code-block:: c++
+
+ switch (ch) {
+ case 'a':
+ case 'A':
+ return 10;
+ case 'b':
+ case 'B':
+ return 11;
+ default:
+ return 10;
+ }
+
+Here the check does not warn for the repeated ``return 10;``, which is good if
+we want to preserve that ``'a'`` is before ``'b'`` and ``default:`` is the last
+branch.
+
+Finally, the check also examines conditional operators and reports code like:
+
+.. code-block:: c++
+
+ return test_value(x) ? x : x;
+
+Unlike if statements, the check does not detect chains of conditional
+operators.
+
+Note: This check also reports situations where branches become identical only
+after preprocession.
Modified: clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst?rev=360779&r1=360778&r2=360779&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst (original)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst Wed May 15 08:06:25 2019
@@ -38,6 +38,7 @@ Clang-Tidy Checks
bugprone-argument-comment
bugprone-assert-side-effect
bugprone-bool-pointer-implicit-conversion
+ bugprone-branch-clone
bugprone-copy-constructor-init
bugprone-dangling-handle
bugprone-exception-escape
Added: clang-tools-extra/trunk/test/clang-tidy/bugprone-branch-clone.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/bugprone-branch-clone.cpp?rev=360779&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/bugprone-branch-clone.cpp (added)
+++ clang-tools-extra/trunk/test/clang-tidy/bugprone-branch-clone.cpp Wed May 15 08:06:25 2019
@@ -0,0 +1,1026 @@
+// RUN: %check_clang_tidy %s bugprone-branch-clone %t
+
+void test_basic1(int in, int &out) {
+ if (in > 77)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+ out++;
+ else
+// CHECK-MESSAGES: :[[@LINE-1]]:3: note: else branch starts here
+ out++;
+
+ out++;
+}
+
+void test_basic2(int in, int &out) {
+ if (in > 77) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+ out++;
+ }
+ else {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: note: else branch starts here
+ out++;
+ }
+
+ out++;
+}
+
+void test_basic3(int in, int &out) {
+ if (in > 77) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+ out++;
+ }
+ else
+// CHECK-MESSAGES: :[[@LINE-1]]:3: note: else branch starts here
+ out++;
+
+ out++;
+}
+
+void test_basic4(int in, int &out) {
+ if (in > 77) {
+ out--;
+ }
+ else {
+ out++;
+ }
+}
+
+void test_basic5(int in, int &out) {
+ if (in > 77) {
+ out++;
+ }
+ else {
+ out++;
+ out++;
+ }
+}
+
+void test_basic6(int in, int &out) {
+ if (in > 77) {
+ out++;
+ }
+ else {
+ out++, out++;
+ }
+}
+
+void test_basic7(int in, int &out) {
+ if (in > 77) {
+ out++;
+ out++;
+ }
+ else
+ out++;
+
+ out++;
+}
+
+void test_basic8(int in, int &out) {
+ if (in > 77) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+ out++;
+ out++;
+ } else {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: note: else branch starts here
+ out++;
+ out++;
+ }
+
+ if (in % 2)
+ out++;
+}
+
+void test_basic9(int in, int &out) {
+ if (in > 77) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+ if (in % 2)
+ out++;
+ else
+ out--;
+ } else {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: note: else branch starts here
+ if (in % 2)
+ out++;
+ else
+ out--;
+ }
+}
+
+// If we remove the braces from the previous example, the check recognizes it
+// as an `else if`.
+void test_basic10(int in, int &out) {
+ if (in > 77)
+ if (in % 2)
+ out++;
+ else
+ out--;
+ else
+ if (in % 2)
+ out++;
+ else
+ out--;
+
+}
+
+void test_basic11(int in, int &out) {
+ if (in > 77) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+ if (in % 2)
+ out++;
+ else
+ out--;
+ if (in % 3)
+ out++;
+ else
+ out--;
+ } else {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: note: else branch starts here
+ if (in % 2)
+ out++;
+ else
+ out--;
+ if (in % 3)
+ out++;
+ else
+ out--;
+ }
+}
+
+void test_basic12(int in, int &out) {
+ if (in > 77) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+ } else {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: note: else branch starts here
+ }
+}
+
+void test_basic13(int in, int &out) {
+ if (in > 77) {
+ // Empty compound statement is not identical to null statement.
+ } else;
+}
+
+// We use a comparison that ignores redundant parentheses:
+void test_basic14(int in, int &out) {
+ if (in > 77)
+ out += 2;
+ else
+ (out) += (2);
+}
+
+void test_basic15(int in, int &out) {
+ if (in > 77)
+ ((out += 2));
+ else
+ out += 2;
+}
+
+// ..but does not apply additional simplifications:
+void test_basic16(int in, int &out) {
+ if (in > 77)
+ out += 2;
+ else
+ out += 1 + 1;
+}
+
+// ..and does not forget important parentheses:
+int test_basic17(int a, int b, int c, int mode) {
+ if (mode>8)
+ return (a + b) * c;
+ else
+ return a + b * c;
+}
+
+//=========--------------------==========//
+
+#define PASTE_CODE(x) x
+
+void test_macro1(int in, int &out) {
+ PASTE_CODE(
+ if (in > 77)
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: if with identical then and else branches [bugprone-branch-clone]
+ out++;
+ else
+// CHECK-MESSAGES: :[[@LINE-1]]:5: note: else branch starts here
+ out++;
+ )
+
+ out--;
+}
+
+void test_macro2(int in, int &out) {
+ PASTE_CODE(
+ if (in > 77)
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: if with identical then and else branches [bugprone-branch-clone]
+ out++;
+ )
+ else
+// CHECK-MESSAGES: :[[@LINE-1]]:3: note: else branch starts here
+ out++;
+}
+
+void test_macro3(int in, int &out) {
+ if (in > 77)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+ out++;
+ PASTE_CODE(
+ else
+// CHECK-MESSAGES: :[[@LINE-1]]:5: note: else branch starts here
+ out++;
+ )
+}
+
+void test_macro4(int in, int &out) {
+ if (in > 77)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+ out++;
+ else
+// CHECK-MESSAGES: :[[@LINE-1]]:3: note: else branch starts here
+ PASTE_CODE(
+ out++;
+ )
+}
+
+void test_macro5(int in, int &out) {
+ PASTE_CODE(if) (in > 77)
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: if with identical then and else branches [bugprone-branch-clone]
+ out++;
+ PASTE_CODE(else)
+// CHECK-MESSAGES: :[[@LINE-1]]:14: note: else branch starts here
+ out++;
+}
+
+#define OTHERWISE_INCREASE else out++
+
+void test_macro6(int in, int &out) {
+ if (in > 77)
+ out++;
+// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+ OTHERWISE_INCREASE;
+// CHECK-MESSAGES: :[[@LINE-1]]:3: note: else branch starts here
+// CHECK-MESSAGES: :[[@LINE-8]]:28: note: expanded from macro 'OTHERWISE_INCREASE'
+}
+
+#define COND_INCR(a, b, c) \
+ do { \
+ if ((a)) \
+ (b)++; \
+ else \
+ (c)++; \
+ } while (0)
+
+void test_macro7(int in, int &out1, int &out2) {
+ COND_INCR(in, out1, out1);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+// CHECK-MESSAGES: :[[@LINE-9]]:5: note: expanded from macro 'COND_INCR'
+// CHECK-MESSAGES: :[[@LINE-3]]:3: note: else branch starts here
+// CHECK-MESSAGES: :[[@LINE-9]]:5: note: expanded from macro 'COND_INCR'
+}
+
+void test_macro8(int in, int &out1, int &out2) {
+ COND_INCR(in, out1, out2);
+}
+
+void test_macro9(int in, int &out1, int &out2) {
+ COND_INCR(in, out2, out2);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+// CHECK-MESSAGES: :[[@LINE-21]]:5: note: expanded from macro 'COND_INCR'
+// CHECK-MESSAGES: :[[@LINE-3]]:3: note: else branch starts here
+// CHECK-MESSAGES: :[[@LINE-21]]:5: note: expanded from macro 'COND_INCR'
+}
+
+#define CONCAT(a, b) a##b
+
+void test_macro10(int in, int &out) {
+ CONCAT(i, f) (in > 77)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+ out++;
+ CONCAT(el, se)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: note: else branch starts here
+ out++;
+}
+
+#define PROBLEM (-1)
+
+int test_macro11(int count) {
+ if (!count)
+ return PROBLEM;
+ else if (count == 13)
+ return -1;
+ else
+ return count * 2;
+}
+
+#define IF if (
+#define THEN ) {
+#define ELSE } else {
+#define END }
+
+void test_macro12(int in, int &out) {
+ IF in > 77 THEN
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+// CHECK-MESSAGES: :[[@LINE-8]]:12: note: expanded from macro 'IF'
+ out++;
+ out++;
+ ELSE
+// CHECK-MESSAGES: :[[@LINE-1]]:3: note: else branch starts here
+// CHECK-MESSAGES: :[[@LINE-11]]:16: note: expanded from macro 'ELSE'
+ out++;
+ out++;
+ END
+}
+
+// A hack for implementing a switch with no fallthrough:
+#define SWITCH(x) switch (x) {
+#define CASE(x) break; case (x):
+#define DEFAULT break; default:
+
+void test_macro13(int in, int &out) {
+ SWITCH(in)
+// CHECK-MESSAGES: :[[@LINE+1]]:5: warning: switch has 3 consecutive identical branches [bugprone-branch-clone]
+ CASE(1)
+ out++;
+ out++;
+ CASE(2)
+ out++;
+ out++;
+ CASE(3)
+ out++;
+ out++;
+// CHECK-MESSAGES: :[[@LINE-15]]:24: note: expanded from macro 'CASE'
+// CHECK-MESSAGES: :[[@LINE+1]]:9: note: last of these clones ends here
+ CASE(4)
+ out++;
+ CASE(5)
+// CHECK-MESSAGES: :[[@LINE+1]]:5: warning: switch has 2 consecutive identical branches [bugprone-branch-clone]
+ CASE(6)
+ out--;
+ CASE(7)
+ out--;
+// CHECK-MESSAGES: :[[@LINE-25]]:24: note: expanded from macro 'CASE'
+// CHECK-MESSAGES: :[[@LINE+2]]:9: note: last of these clones ends here
+// CHECK-MESSAGES: :[[@LINE+1]]:5: warning: switch has 2 consecutive identical branches [bugprone-branch-clone]
+ CASE(8)
+ out++;
+ out++;
+ CASE(9)
+ out++;
+ out++;
+// CHECK-MESSAGES: :[[@LINE-34]]:24: note: expanded from macro 'CASE'
+// CHECK-MESSAGES: :[[@LINE+2]]:12: note: last of these clones ends here
+// CHECK-MESSAGES: :[[@LINE+1]]:5: warning: switch has 2 consecutive identical branches [bugprone-branch-clone]
+ DEFAULT
+ out--;
+ out--;
+ CASE(10)
+ out--;
+ out--;
+// CHECK-MESSAGES: :[[@LINE-42]]:24: note: expanded from macro 'DEFAULT'
+// CHECK-MESSAGES: :[[@LINE+1]]:9: note: last of these clones ends here
+ CASE(12)
+ out++;
+ CASE(13)
+ out++;
+ END
+}
+
+//=========--------------------==========//
+
+void test_chain1(int in, int &out) {
+ if (in > 77)
+// CHECK-MESSAGES: :[[@LINE+1]]:5: warning: repeated branch in conditional chain [bugprone-branch-clone]
+ out++;
+// CHECK-MESSAGES: :[[@LINE-1]]:10: note: end of the original
+ else if (in > 55)
+// CHECK-MESSAGES: :[[@LINE+1]]:5: note: clone 1 starts here
+ out++;
+
+ out++;
+}
+
+void test_chain2(int in, int &out) {
+ if (in > 77)
+// CHECK-MESSAGES: :[[@LINE+1]]:5: warning: repeated branch in conditional chain [bugprone-branch-clone]
+ out++;
+// CHECK-MESSAGES: :[[@LINE-1]]:10: note: end of the original
+ else if (in > 55)
+// CHECK-MESSAGES: :[[@LINE+1]]:5: note: clone 1 starts here
+ out++;
+ else if (in > 42)
+ out--;
+ else if (in > 28)
+// CHECK-MESSAGES: :[[@LINE+1]]:5: note: clone 2 starts here
+ out++;
+ else if (in > 12) {
+ out++;
+ out *= 7;
+ } else if (in > 7) {
+// CHECK-MESSAGES: :[[@LINE-1]]:22: note: clone 3 starts here
+ out++;
+ }
+}
+
+void test_chain3(int in, int &out) {
+ if (in > 77) {
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: repeated branch in conditional chain [bugprone-branch-clone]
+ out++;
+ out++;
+// CHECK-MESSAGES: :[[@LINE+1]]:4: note: end of the original
+ } else if (in > 55) {
+// CHECK-MESSAGES: :[[@LINE-1]]:23: note: clone 1 starts here
+ out++;
+ out++;
+ } else if (in > 42)
+ out--;
+ else if (in > 28) {
+// CHECK-MESSAGES: :[[@LINE-1]]:21: note: clone 2 starts here
+ out++;
+ out++;
+ } else if (in > 12) {
+ out++;
+ out++;
+ out++;
+ out *= 7;
+ } else if (in > 7) {
+// CHECK-MESSAGES: :[[@LINE-1]]:22: note: clone 3 starts here
+ out++;
+ out++;
+ }
+}
+
+// In this chain there are two clone families; notice that the checker
+// describes all branches of the first one before mentioning the second one.
+void test_chain4(int in, int &out) {
+ if (in > 77) {
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: repeated branch in conditional chain [bugprone-branch-clone]
+ out++;
+ out++;
+// CHECK-MESSAGES: :[[@LINE+1]]:4: note: end of the original
+ } else if (in > 55) {
+// CHECK-MESSAGES: :[[@LINE-1]]:23: note: clone 1 starts here
+// CHECK-MESSAGES: :[[@LINE+8]]:21: note: clone 2 starts here
+// CHECK-MESSAGES: :[[@LINE+15]]:22: note: clone 3 starts here
+ out++;
+ out++;
+ } else if (in > 42)
+// CHECK-MESSAGES: :[[@LINE+1]]:5: warning: repeated branch in conditional chain [bugprone-branch-clone]
+ out--;
+// CHECK-MESSAGES: :[[@LINE-1]]:10: note: end of the original
+ else if (in > 28) {
+ out++;
+ out++;
+ } else if (in > 12) {
+ out++;
+ out++;
+ out++;
+ out *= 7;
+ } else if (in > 7) {
+ out++;
+ out++;
+ } else if (in > -3) {
+// CHECK-MESSAGES: :[[@LINE-1]]:23: note: clone 1 starts here
+ out--;
+ }
+}
+
+void test_chain5(int in, int &out) {
+ if (in > 77)
+// CHECK-MESSAGES: :[[@LINE+1]]:5: warning: repeated branch in conditional chain [bugprone-branch-clone]
+ out++;
+// CHECK-MESSAGES: :[[@LINE-1]]:10: note: end of the original
+ else if (in > 55)
+// CHECK-MESSAGES: :[[@LINE+1]]:5: note: clone 1 starts here
+ out++;
+ else if (in > 42)
+ out--;
+ else if (in > 28)
+// CHECK-MESSAGES: :[[@LINE+1]]:5: note: clone 2 starts here
+ out++;
+ else if (in > 12) {
+ out++;
+ out *= 7;
+ } else {
+// CHECK-MESSAGES: :[[@LINE-1]]:10: note: clone 3 starts here
+ out++;
+ }
+}
+
+void test_chain6(int in, int &out) {
+ if (in > 77) {
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: repeated branch in conditional chain [bugprone-branch-clone]
+ out++;
+ out++;
+// CHECK-MESSAGES: :[[@LINE+1]]:4: note: end of the original
+ } else if (in > 55) {
+// CHECK-MESSAGES: :[[@LINE-1]]:23: note: clone 1 starts here
+ out++;
+ out++;
+ } else if (in > 42)
+ out--;
+ else if (in > 28) {
+// CHECK-MESSAGES: :[[@LINE-1]]:21: note: clone 2 starts here
+ out++;
+ out++;
+ } else if (in > 12) {
+ out++;
+ out++;
+ out++;
+ out *= 7;
+ } else {
+// CHECK-MESSAGES: :[[@LINE-1]]:10: note: clone 3 starts here
+ out++;
+ out++;
+ }
+}
+
+void test_nested(int a, int b, int c, int &out) {
+ if (a > 5) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+// CHECK-MESSAGES: :[[@LINE+27]]:5: note: else branch starts here
+ if (b > 5) {
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: repeated branch in conditional chain [bugprone-branch-clone]
+// CHECK-MESSAGES: :[[@LINE+9]]:6: note: end of the original
+// CHECK-MESSAGES: :[[@LINE+8]]:24: note: clone 1 starts here
+// CHECK-MESSAGES: :[[@LINE+14]]:12: note: clone 2 starts here
+ if (c > 5)
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: if with identical then and else branches [bugprone-branch-clone]
+ out++;
+ else
+// CHECK-MESSAGES: :[[@LINE-1]]:7: note: else branch starts here
+ out++;
+ } else if (b > 15) {
+ if (c > 5)
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: if with identical then and else branches [bugprone-branch-clone]
+ out++;
+ else
+// CHECK-MESSAGES: :[[@LINE-1]]:7: note: else branch starts here
+ out++;
+ } else {
+ if (c > 5)
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: if with identical then and else branches [bugprone-branch-clone]
+ out++;
+ else
+// CHECK-MESSAGES: :[[@LINE-1]]:7: note: else branch starts here
+ out++;
+ }
+ } else {
+ if (b > 5) {
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: repeated branch in conditional chain [bugprone-branch-clone]
+// CHECK-MESSAGES: :[[@LINE+9]]:6: note: end of the original
+// CHECK-MESSAGES: :[[@LINE+8]]:24: note: clone 1 starts here
+// CHECK-MESSAGES: :[[@LINE+14]]:12: note: clone 2 starts here
+ if (c > 5)
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: if with identical then and else branches [bugprone-branch-clone]
+ out++;
+ else
+// CHECK-MESSAGES: :[[@LINE-1]]:7: note: else branch starts here
+ out++;
+ } else if (b > 15) {
+ if (c > 5)
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: if with identical then and else branches [bugprone-branch-clone]
+ out++;
+ else
+// CHECK-MESSAGES: :[[@LINE-1]]:7: note: else branch starts here
+ out++;
+ } else {
+ if (c > 5)
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: if with identical then and else branches [bugprone-branch-clone]
+ out++;
+ else
+// CHECK-MESSAGES: :[[@LINE-1]]:7: note: else branch starts here
+ out++;
+ }
+ }
+}
+
+//=========--------------------==========//
+
+template <class T>
+void test_template_not_instantiated(const T &t) {
+ int a;
+ if (t)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+ a++;
+ else
+// CHECK-MESSAGES: :[[@LINE-1]]:3: note: else branch starts here
+ a++;
+}
+
+template <class T>
+void test_template_instantiated(const T &t) {
+ int a;
+ if (t)
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+ a++;
+ else
+// CHECK-MESSAGES: :[[@LINE-1]]:3: note: else branch starts here
+ a++;
+}
+
+template void test_template_instantiated<int>(const int &t);
+
+template <class T>
+void test_template2(T t, int a) {
+ if (a) {
+ T b(0);
+ a += b;
+ } else {
+ int b(0);
+ a += b;
+ }
+}
+
+template void test_template2<int>(int t, int a);
+
+template <class T>
+void test_template3(T t, int a) {
+ if (a) {
+ T b(0);
+ a += b;
+ } else {
+ int b(0);
+ a += b;
+ }
+}
+
+template void test_template3<short>(short t, int a);
+
+template <class T>
+void test_template_two_instances(T t, int &a) {
+ if (a) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+ a += int(t);
+ } else {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: note: else branch starts here
+ a += int(t);
+ }
+}
+
+template void test_template_two_instances<short>(short t, int &a);
+template void test_template_two_instances<long>(long t, int &a);
+
+class C {
+ int member;
+ void inline_method(int arg) {
+ if (arg)
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: if with identical then and else branches [bugprone-branch-clone]
+ member = 3;
+ else
+// CHECK-MESSAGES: :[[@LINE-1]]:5: note: else branch starts here
+ member = 3;
+ }
+ int other_method();
+};
+
+int C::other_method() {
+ if (member) {
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
+ return 8;
+ } else {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: note: else branch starts here
+ return 8;
+ }
+}
+
+//=========--------------------==========//
+
+int simple_switch(char ch) {
+ switch (ch) {
+// CHECK-MESSAGES: :[[@LINE+1]]:3: warning: switch has 2 consecutive identical branches [bugprone-branch-clone]
+ case 'a':
+ return 10;
+ case 'A':
+ return 10;
+// CHECK-MESSAGES: :[[@LINE-1]]:14: note: last of these clones ends here
+// CHECK-MESSAGES: :[[@LINE+1]]:3: warning: switch has 2 consecutive identical branches [bugprone-branch-clone]
+ case 'b':
+ return 11;
+ case 'B':
+ return 11;
+// CHECK-MESSAGES: :[[@LINE-1]]:14: note: last of these clones ends here
+// CHECK-MESSAGES: :[[@LINE+1]]:3: warning: switch has 2 consecutive identical branches [bugprone-branch-clone]
+ case 'c':
+ return 10;
+ case 'C':
+ return 10;
+// CHECK-MESSAGES: :[[@LINE-1]]:14: note: last of these clones ends here
+ default:
+ return 0;
+ }
+}
+
+int long_sequence_switch(char ch) {
+ switch (ch) {
+// CHECK-MESSAGES: :[[@LINE+1]]:3: warning: switch has 7 consecutive identical branches [bugprone-branch-clone]
+ case 'a':
+ return 10;
+ case 'A':
+ return 10;
+ case 'b':
+ return 10;
+ case 'B':
+ return 10;
+ case 'c':
+ return 10;
+ case 'C':
+ return 10;
+ default:
+ return 10;
+// CHECK-MESSAGES: :[[@LINE-1]]:14: note: last of these clones ends here
+ }
+}
+
+int nested_switch(int a, int b, int c) {
+ switch (a) {
+// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: switch has 3 consecutive identical branches [bugprone-branch-clone]
+// CHECK-MESSAGES: :[[@LINE+114]]:6: note: last of these clones ends here
+ case 1:
+ switch (b) {
+// CHECK-MESSAGES: :[[@LINE+2]]:5: warning: switch has 3 consecutive identical branches [bugprone-branch-clone]
+// CHECK-MESSAGES: :[[@LINE+33]]:8: note: last of these clones ends here
+ case 1:
+ switch (c) {
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: switch has 3 consecutive identical branches [bugprone-branch-clone]
+ case 1:
+ return 42;
+ case 2:
+ return 42;
+ default:
+ return 42;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: note: last of these clones ends here
+ }
+ case 2:
+ switch (c) {
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: switch has 3 consecutive identical branches [bugprone-branch-clone]
+ case 1:
+ return 42;
+ case 2:
+ return 42;
+ default:
+ return 42;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: note: last of these clones ends here
+ }
+ default:
+ switch (c) {
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: switch has 3 consecutive identical branches [bugprone-branch-clone]
+ case 1:
+ return 42;
+ case 2:
+ return 42;
+ default:
+ return 42;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: note: last of these clones ends here
+ }
+ }
+ case 2:
+ switch (b) {
+// CHECK-MESSAGES: :[[@LINE+2]]:5: warning: switch has 3 consecutive identical branches [bugprone-branch-clone]
+// CHECK-MESSAGES: :[[@LINE+33]]:8: note: last of these clones ends here
+ case 1:
+ switch (c) {
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: switch has 3 consecutive identical branches [bugprone-branch-clone]
+ case 1:
+ return 42;
+ case 2:
+ return 42;
+ default:
+ return 42;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: note: last of these clones ends here
+ }
+ case 2:
+ switch (c) {
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: switch has 3 consecutive identical branches [bugprone-branch-clone]
+ case 1:
+ return 42;
+ case 2:
+ return 42;
+ default:
+ return 42;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: note: last of these clones ends here
+ }
+ default:
+ switch (c) {
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: switch has 3 consecutive identical branches [bugprone-branch-clone]
+ case 1:
+ return 42;
+ case 2:
+ return 42;
+ default:
+ return 42;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: note: last of these clones ends here
+ }
+ }
+ default:
+ switch (b) {
+// CHECK-MESSAGES: :[[@LINE+2]]:5: warning: switch has 3 consecutive identical branches [bugprone-branch-clone]
+// CHECK-MESSAGES: :[[@LINE+33]]:8: note: last of these clones ends here
+ case 1:
+ switch (c) {
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: switch has 3 consecutive identical branches [bugprone-branch-clone]
+ case 1:
+ return 42;
+ case 2:
+ return 42;
+ default:
+ return 42;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: note: last of these clones ends here
+ }
+ case 2:
+ switch (c) {
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: switch has 3 consecutive identical branches [bugprone-branch-clone]
+ case 1:
+ return 42;
+ case 2:
+ return 42;
+ default:
+ return 42;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: note: last of these clones ends here
+ }
+ default:
+ switch (c) {
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: switch has 3 consecutive identical branches [bugprone-branch-clone]
+ case 1:
+ return 42;
+ case 2:
+ return 42;
+ default:
+ return 42;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: note: last of these clones ends here
+ }
+ }
+ }
+}
+
+//=========--------------------==========//
+
+// This should not produce warnings, as in switch statements we only report
+// identical branches when they are consecutive. Also note that a branch
+// terminated by a break is different from a branch terminated by the end of
+// the switch statement.
+int interleaved_cases(int a, int b) {
+ switch (a) {
+ case 3:
+ case 4:
+ b = 2;
+ break;
+ case 5:
+ b = 3;
+ break;
+ case 6:
+ b = 2;
+ break;
+ case 7:
+ if (b % 2) {
+ b++;
+ } else {
+ b++;
+ break;
+ }
+ b = 2;
+ break;
+ case 8:
+ b = 2;
+ case 9:
+ b = 3;
+ break;
+ default:
+ b = 3;
+ }
+ return b;
+}
+
+
+// A case: or default: is only considered to be the start of a branch if it is a direct child of the CompoundStmt forming the body of the switch
+int buried_cases(int foo) {
+ switch (foo) {
+ {
+ case 36:
+ return 8;
+ default:
+ return 8;
+ }
+ }
+}
+
+// Here the `case 7:` is a child statement of the GotoLabelStmt, so the checker
+// thinks that it is part of the `case 9:` branch. While this result is
+// counterintuitve, mixing goto labels and switch statements in this fashion is
+// pretty rare, so it does not deserve a special case in the checker code.
+int decorated_cases(int z) {
+ if (!(z % 777)) {
+ goto lucky;
+ }
+ switch (z) {
+// CHECK-MESSAGES: :[[@LINE+1]]:3: warning: switch has 2 consecutive identical branches [bugprone-branch-clone]
+ case 1:
+ case 2:
+ case 3:
+ z++;
+ break;
+ case 4:
+ case 5:
+ z++;
+ break;
+// CHECK-MESSAGES: :[[@LINE-1]]:10: note: last of these clones ends here
+ case 9:
+ z++;
+ break;
+ lucky:
+ case 7:
+ z += 3;
+ z *= 2;
+ break;
+ case 92:
+ z += 3;
+ z *= 2;
+ break;
+ default:
+ z++;
+ }
+ return z + 92;
+}
+
+// The child of the switch statement is not neccessarily a compound statement,
+// do not crash in this unusual case.
+char no_real_body(int in, int &out) {
+ switch (in)
+ case 42:
+ return 'A';
+
+ if (in > 77)
+// CHECK-MESSAGES: :[[@LINE+1]]:5: warning: repeated branch in conditional chain [bugprone-branch-clone]
+ out++;
+// CHECK-MESSAGES: :[[@LINE-1]]:10: note: end of the original
+ else if (in > 55)
+// CHECK-MESSAGES: :[[@LINE+1]]:5: note: clone 1 starts here
+ out++;
+ else if (in > 34)
+// CHECK-MESSAGES: :[[@LINE+1]]:5: note: clone 2 starts here
+ out++;
+
+ return '|';
+}
+
+// Duff's device [https://en.wikipedia.org/wiki/Duff's_device]
+// The check does not try to distinguish branches in this sort of convoluted
+// code, but it should avoid crashing.
+void send(short *to, short *from, int count)
+{
+ int n = (count + 7) / 8;
+ switch (count % 8) {
+ case 0: do { *to = *from++;
+ case 7: *to = *from++;
+ case 6: *to = *from++;
+ case 5: *to = *from++;
+ case 4: *to = *from++;
+ case 3: *to = *from++;
+ case 2: *to = *from++;
+ case 1: *to = *from++;
+ } while (--n > 0);
+ }
+}
+
+//=========--------------------==========//
+
+void ternary1(bool b, int &x) {
+// CHECK-MESSAGES: :[[@LINE+1]]:6: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
+ (b ? x : x) = 42;
+}
+
+int ternary2(bool b, int x) {
+// CHECK-MESSAGES: :[[@LINE+1]]:12: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
+ return b ? 42 : 42;
+}
+
+int ternary3(bool b, int x) {
+ return b ? 42 : 43;
+}
+
+int ternary4(bool b, int x) {
+ return b ? true ? 45 : 44 : false ? 45 : 44;
+}
+
+// We do not detect chains of conditional operators.
+int ternary5(bool b1, bool b2, int x) {
+ return b1 ? 42 : b2 ? 43 : 42;
+}
+
+#define SWITCH_WITH_LBRACE(b) switch (b) {
+#define SEMICOLON_CASE_COLON(b) \
+ ; \
+ case b:
+int d, e;
+void dontCrash() {
+ SWITCH_WITH_LBRACE(d)
+// CHECK-MESSAGES: :[[@LINE+1]]:3: warning: switch has 2 consecutive identical branches [bugprone-branch-clone]
+ SEMICOLON_CASE_COLON(1)
+ e++;
+ e++;
+ SEMICOLON_CASE_COLON(2)
+ e++;
+ e++;
+ // CHECK-MESSAGES: :[[@LINE-11]]:3: note: expanded from macro 'SEMICOLON_CASE_COLON'
+ // CHECK-MESSAGES: :[[@LINE+1]]:23: note: last of these clones ends here
+ SEMICOLON_CASE_COLON(3);
+ }
+}
More information about the cfe-commits
mailing list