[analyzer] Remove alpha.core.IdenticalExpr Checker (#114715)

This change removes the alpha.core.IdenticalExpr static analysis checker
since it's checks are present in the clang-tidy checks
misc-redundant-expression and bugprone-branch-clone. This check was
implemented as a static analysis check using AST matching, and since
alpha and duplicated in 2 clang-tidy checks may be removed.

Co-authored-by: Vince Bridgers <vince.a.bridgers@ericsson.com>
This commit is contained in:
vabridgers
2024-11-29 17:38:59 -06:00
committed by GitHub
parent 52690db47d
commit 8ac2b77a11
11 changed files with 570 additions and 663 deletions

View File

@@ -102,6 +102,212 @@ void BranchCloneCheck::registerMatchers(MatchFinder *Finder) {
this);
Finder->addMatcher(switchStmt().bind("switch"), this);
Finder->addMatcher(conditionalOperator().bind("condOp"), this);
Finder->addMatcher(
ifStmt((hasThen(hasDescendant(ifStmt())))).bind("ifWithDescendantIf"),
this);
}
/// Determines whether two statement trees are identical regarding
/// operators and symbols.
///
/// Exceptions: expressions containing macros or functions with possible side
/// effects are never considered identical.
/// Limitations: (t + u) and (u + t) are not considered identical.
/// t*(u + t) and t*u + t*t are not considered identical.
///
static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
const Stmt *Stmt2, bool IgnoreSideEffects) {
if (!Stmt1 || !Stmt2)
return !Stmt1 && !Stmt2;
// If Stmt1 & Stmt2 are of different class then they are not
// identical statements.
if (Stmt1->getStmtClass() != Stmt2->getStmtClass())
return false;
const auto *Expr1 = dyn_cast<Expr>(Stmt1);
const auto *Expr2 = dyn_cast<Expr>(Stmt2);
if (Expr1 && Expr2) {
// If Stmt1 has side effects then don't warn even if expressions
// are identical.
if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx) &&
Expr2->HasSideEffects(Ctx))
return false;
// If either expression comes from a macro then don't warn even if
// the expressions are identical.
if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID()))
return false;
// If all children of two expressions are identical, return true.
Expr::const_child_iterator I1 = Expr1->child_begin();
Expr::const_child_iterator I2 = Expr2->child_begin();
while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) {
if (!isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))
return false;
++I1;
++I2;
}
// If there are different number of children in the statements, return
// false.
if (I1 != Expr1->child_end())
return false;
if (I2 != Expr2->child_end())
return false;
}
switch (Stmt1->getStmtClass()) {
default:
return false;
case Stmt::CallExprClass:
case Stmt::ArraySubscriptExprClass:
case Stmt::ArraySectionExprClass:
case Stmt::OMPArrayShapingExprClass:
case Stmt::OMPIteratorExprClass:
case Stmt::ImplicitCastExprClass:
case Stmt::ParenExprClass:
case Stmt::BreakStmtClass:
case Stmt::ContinueStmtClass:
case Stmt::NullStmtClass:
return true;
case Stmt::CStyleCastExprClass: {
const auto *CastExpr1 = cast<CStyleCastExpr>(Stmt1);
const auto *CastExpr2 = cast<CStyleCastExpr>(Stmt2);
return CastExpr1->getTypeAsWritten() == CastExpr2->getTypeAsWritten();
}
case Stmt::ReturnStmtClass: {
const auto *ReturnStmt1 = cast<ReturnStmt>(Stmt1);
const auto *ReturnStmt2 = cast<ReturnStmt>(Stmt2);
return isIdenticalStmt(Ctx, ReturnStmt1->getRetValue(),
ReturnStmt2->getRetValue(), IgnoreSideEffects);
}
case Stmt::ForStmtClass: {
const auto *ForStmt1 = cast<ForStmt>(Stmt1);
const auto *ForStmt2 = cast<ForStmt>(Stmt2);
if (!isIdenticalStmt(Ctx, ForStmt1->getInit(), ForStmt2->getInit(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, ForStmt1->getCond(), ForStmt2->getCond(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, ForStmt1->getInc(), ForStmt2->getInc(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, ForStmt1->getBody(), ForStmt2->getBody(),
IgnoreSideEffects))
return false;
return true;
}
case Stmt::DoStmtClass: {
const auto *DStmt1 = cast<DoStmt>(Stmt1);
const auto *DStmt2 = cast<DoStmt>(Stmt2);
if (!isIdenticalStmt(Ctx, DStmt1->getCond(), DStmt2->getCond(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, DStmt1->getBody(), DStmt2->getBody(),
IgnoreSideEffects))
return false;
return true;
}
case Stmt::WhileStmtClass: {
const auto *WStmt1 = cast<WhileStmt>(Stmt1);
const auto *WStmt2 = cast<WhileStmt>(Stmt2);
if (!isIdenticalStmt(Ctx, WStmt1->getCond(), WStmt2->getCond(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, WStmt1->getBody(), WStmt2->getBody(),
IgnoreSideEffects))
return false;
return true;
}
case Stmt::IfStmtClass: {
const auto *IStmt1 = cast<IfStmt>(Stmt1);
const auto *IStmt2 = cast<IfStmt>(Stmt2);
if (!isIdenticalStmt(Ctx, IStmt1->getCond(), IStmt2->getCond(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, IStmt1->getThen(), IStmt2->getThen(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, IStmt1->getElse(), IStmt2->getElse(),
IgnoreSideEffects))
return false;
return true;
}
case Stmt::CompoundStmtClass: {
const auto *CompStmt1 = cast<CompoundStmt>(Stmt1);
const auto *CompStmt2 = cast<CompoundStmt>(Stmt2);
if (CompStmt1->size() != CompStmt2->size())
return false;
if (!llvm::all_of(llvm::zip(CompStmt1->body(), CompStmt2->body()),
[&Ctx, IgnoreSideEffects](
std::tuple<const Stmt *, const Stmt *> stmtPair) {
const Stmt *stmt0 = std::get<0>(stmtPair);
const Stmt *stmt1 = std::get<1>(stmtPair);
return isIdenticalStmt(Ctx, stmt0, stmt1,
IgnoreSideEffects);
})) {
return false;
}
return true;
}
case Stmt::CompoundAssignOperatorClass:
case Stmt::BinaryOperatorClass: {
const auto *BinOp1 = cast<BinaryOperator>(Stmt1);
const auto *BinOp2 = cast<BinaryOperator>(Stmt2);
return BinOp1->getOpcode() == BinOp2->getOpcode();
}
case Stmt::CharacterLiteralClass: {
const auto *CharLit1 = cast<CharacterLiteral>(Stmt1);
const auto *CharLit2 = cast<CharacterLiteral>(Stmt2);
return CharLit1->getValue() == CharLit2->getValue();
}
case Stmt::DeclRefExprClass: {
const auto *DeclRef1 = cast<DeclRefExpr>(Stmt1);
const auto *DeclRef2 = cast<DeclRefExpr>(Stmt2);
return DeclRef1->getDecl() == DeclRef2->getDecl();
}
case Stmt::IntegerLiteralClass: {
const auto *IntLit1 = cast<IntegerLiteral>(Stmt1);
const auto *IntLit2 = cast<IntegerLiteral>(Stmt2);
llvm::APInt I1 = IntLit1->getValue();
llvm::APInt I2 = IntLit2->getValue();
if (I1.getBitWidth() != I2.getBitWidth())
return false;
return I1 == I2;
}
case Stmt::FloatingLiteralClass: {
const auto *FloatLit1 = cast<FloatingLiteral>(Stmt1);
const auto *FloatLit2 = cast<FloatingLiteral>(Stmt2);
return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue());
}
case Stmt::StringLiteralClass: {
const auto *StringLit1 = cast<StringLiteral>(Stmt1);
const auto *StringLit2 = cast<StringLiteral>(Stmt2);
return StringLit1->getBytes() == StringLit2->getBytes();
}
case Stmt::MemberExprClass: {
const auto *MemberStmt1 = cast<MemberExpr>(Stmt1);
const auto *MemberStmt2 = cast<MemberExpr>(Stmt2);
return MemberStmt1->getMemberDecl() == MemberStmt2->getMemberDecl();
}
case Stmt::UnaryOperatorClass: {
const auto *UnaryOp1 = cast<UnaryOperator>(Stmt1);
const auto *UnaryOp2 = cast<UnaryOperator>(Stmt2);
return UnaryOp1->getOpcode() == UnaryOp2->getOpcode();
}
}
}
void BranchCloneCheck::check(const MatchFinder::MatchResult &Result) {
@@ -269,6 +475,21 @@ void BranchCloneCheck::check(const MatchFinder::MatchResult &Result) {
return;
}
if (const auto *IS = Result.Nodes.getNodeAs<IfStmt>("ifWithDescendantIf")) {
const Stmt *Then = IS->getThen();
auto CS = dyn_cast<CompoundStmt>(Then);
if (CS && (!CS->body_empty())) {
const auto *InnerIf = dyn_cast<IfStmt>(*CS->body_begin());
if (InnerIf && isIdenticalStmt(Context, IS->getCond(), InnerIf->getCond(),
/*IgnoreSideEffects=*/false)) {
diag(IS->getBeginLoc(), "if with identical inner if statement");
diag(InnerIf->getBeginLoc(), "inner if starts here",
DiagnosticIDs::Note);
}
}
return;
}
llvm_unreachable("No if statement and no switch statement.");
}

View File

@@ -855,9 +855,6 @@ static bool areExprsMacroAndNonMacro(const Expr *&LhsExpr,
} // namespace
void RedundantExpressionCheck::registerMatchers(MatchFinder *Finder) {
const auto AnyLiteralExpr = ignoringParenImpCasts(
anyOf(cxxBoolLiteral(), characterLiteral(), integerLiteral()));
const auto BannedIntegerLiteral =
integerLiteral(expandedByMacro(KnownBannedMacroNames));
const auto IsInUnevaluatedContext = expr(anyOf(
@@ -866,19 +863,16 @@ void RedundantExpressionCheck::registerMatchers(MatchFinder *Finder) {
// Binary with equivalent operands, like (X != 2 && X != 2).
Finder->addMatcher(
traverse(TK_AsIs,
binaryOperator(
anyOf(isComparisonOperator(),
hasAnyOperatorName("-", "/", "%", "|", "&", "^", "&&",
"||", "=")),
operandsAreEquivalent(),
// Filter noisy false positives.
unless(isInTemplateInstantiation()),
unless(binaryOperatorIsInMacro()),
unless(hasType(realFloatingPointType())),
unless(hasEitherOperand(hasType(realFloatingPointType()))),
unless(hasLHS(AnyLiteralExpr)),
unless(hasDescendant(BannedIntegerLiteral)),
unless(IsInUnevaluatedContext))
binaryOperator(anyOf(isComparisonOperator(),
hasAnyOperatorName("-", "/", "%", "|", "&",
"^", "&&", "||", "=")),
operandsAreEquivalent(),
// Filter noisy false positives.
unless(isInTemplateInstantiation()),
unless(binaryOperatorIsInMacro()),
unless(hasAncestor(arraySubscriptExpr())),
unless(hasDescendant(BannedIntegerLiteral)),
unless(IsInUnevaluatedContext))
.bind("binary")),
this);
@@ -1238,6 +1232,50 @@ void RedundantExpressionCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("binary")) {
// If the expression's constants are macros, check whether they are
// intentional.
//
// Special case for floating-point representation.
//
// If expressions on both sides of comparison operator are of type float,
// then for some comparison operators no warning shall be
// reported even if the expressions are identical from a symbolic point of
// view. Comparison between expressions, declared variables and literals
// are treated differently.
//
// != and == between float literals that have the same value should NOT
// warn. < > between float literals that have the same value SHOULD warn.
//
// != and == between the same float declaration should NOT warn.
// < > between the same float declaration SHOULD warn.
//
// != and == between eq. expressions that evaluates into float
// should NOT warn.
// < > between eq. expressions that evaluates into float
// should NOT warn.
//
const Expr *LHS = BinOp->getLHS()->IgnoreParenImpCasts();
const Expr *RHS = BinOp->getRHS()->IgnoreParenImpCasts();
const BinaryOperator::Opcode Op = BinOp->getOpcode();
const bool OpEqualEQorNE = ((Op == BO_EQ) || (Op == BO_NE));
const auto *DeclRef1 = dyn_cast<DeclRefExpr>(LHS);
const auto *DeclRef2 = dyn_cast<DeclRefExpr>(RHS);
const auto *FloatLit1 = dyn_cast<FloatingLiteral>(LHS);
const auto *FloatLit2 = dyn_cast<FloatingLiteral>(RHS);
if (DeclRef1 && DeclRef2 &&
DeclRef1->getType()->hasFloatingRepresentation() &&
DeclRef2->getType()->hasFloatingRepresentation() &&
(DeclRef1->getDecl() == DeclRef2->getDecl()) && OpEqualEQorNE) {
return;
}
if (FloatLit1 && FloatLit2 &&
FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue()) &&
OpEqualEQorNE) {
return;
}
if (areSidesBinaryConstExpressions(BinOp, Result.Context)) {
const Expr *LhsConst = nullptr, *RhsConst = nullptr;
BinaryOperatorKind MainOpcode{}, SideOpcode{};

View File

@@ -156,6 +156,10 @@ Changes in existing checks
<clang-tidy/checks/altera/id-dependent-backward-branch>` check by fixing
crashes from invalid code.
- Improved :doc:`bugprone-branch-clone
<clang-tidy/checks/bugprone/branch-clone>` check to improve detection of
branch clones by now detecting duplicate inner and outer if statements.
- Improved :doc:`bugprone-casting-through-void
<clang-tidy/checks/bugprone/casting-through-void>` check to suggest replacing
the offending code with ``reinterpret_cast``, to more clearly express intent.
@@ -230,6 +234,11 @@ Changes in existing checks
<clang-tidy/checks/misc/definitions-in-headers>` check by rewording the
diagnostic note that suggests adding ``inline``.
- Improved :doc:`misc-redundant-expression
<clang-tidy/checks/misc/redundant-expression>` check by extending the
checker to detect floating point and integer literals in redundant
expressions.
- Improved :doc:`misc-unconventional-assign-operator
<clang-tidy/checks/misc/unconventional-assign-operator>` check to avoid
false positive for C++23 deducing this.

View File

@@ -32,9 +32,28 @@ If this is the intended 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
The check detects repeated branches in longer ``if/else if/else`` chains
where it would be even harder to notice the problem.
The check also detects repeated inner and outer ``if`` statements that may
be a result of a copy-paste error. This check cannot currently detect
identical inner and outer ``if`` statements if code is between the ``if``
conditions. An example is as follows.
.. code-block:: c++
void test_warn_inner_if_1(int x) {
if (x == 1) { // warns, if with identical inner if
if (x == 1) // inner if is here
;
if (x == 1) { // does not warn, cannot detect
int y = x;
if (x == 1)
;
}
}
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

View File

@@ -23,3 +23,22 @@ Examples:
(p->x == p->x) // always true
(p->x < p->x) // always false
(speed - speed + 1 == 12) // speed - speed is always zero
int b = a | 4 | a // identical expr on both sides
((x=1) | (x=1)) // expression is identical
Floats are handled except in the case that NaNs are checked like so:
.. code-block:: c++
int TestFloat(float F) {
if (F == F) // Identical float values used
return 1;
return 0;
}
int TestFloat(float F) {
// Testing NaN.
if (F != F && F == F) // does not warn
return 1;
return 0;
}

View File

@@ -1,4 +1,5 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core.IdenticalExpr -w -verify %s
// RUN: clang-tidy %s -checks="-*,misc-redundant-expression" -- 2>&1 | FileCheck %s --check-prefix=CHECK-MESSAGES-IDENTEXPR
// RUN: clang-tidy %s -checks="-*,bugprone-branch-clone" -- 2>&1 | FileCheck %s --check-prefix=CHECK-MESSAGES-BUGPRONEBRANCH
/* Only one expected warning per function allowed at the very end. */
@@ -65,7 +66,8 @@ int checkNotEqualFloatDeclCompare6(void) {
int checkNotEqualCastFloatDeclCompare11(void) {
float f = 7.1F;
return ((int)f != (int)f); // expected-warning {{comparison of identical expressions always evaluates to false}}
return ((int)f != (int)f);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:18: warning: both sides of operator are equivalent [misc-redundant-expression]
}
int checkNotEqualCastFloatDeclCompare12(void) {
float f = 7.1F;
@@ -130,7 +132,8 @@ int checkNotEqualNestedBinaryOpFloatCompare3(void) {
/* '!=' with int*/
int checkNotEqualIntLiteralCompare1(void) {
return (5 != 5); // expected-warning {{comparison of identical expressions always evaluates to false}}
return (5 != 5);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:13: warning: both sides of operator are equivalent [misc-redundant-expression]
}
int checkNotEqualIntLiteralCompare2(void) {
@@ -155,7 +158,8 @@ int checkNotEqualIntDeclCompare4(void) {
int checkNotEqualCastIntDeclCompare11(void) {
int f = 7;
return ((int)f != (int)f); // expected-warning {{comparison of identical expressions always evaluates to false}}
return ((int)f != (int)f);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:18: warning: both sides of operator are equivalent [misc-redundant-expression]
}
int checkNotEqualCastIntDeclCompare12(void) {
int f = 7;
@@ -166,7 +170,8 @@ int checkNotEqualBinaryOpIntCompare1(void) {
int t= 1;
int u= 2;
int f= 4;
res = (f + 4 != f + 4); // expected-warning {{comparison of identical expressions always evaluates to false}}
res = (f + 4 != f + 4);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:16: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
int checkNotEqualBinaryOpIntCompare2(void) {
@@ -181,7 +186,8 @@ int checkNotEqualBinaryOpIntCompare3(void) {
int t= 1;
int u= 2;
int f= 4;
res = ((int)f + 4 != (int)f + 4); // expected-warning {{comparison of identical expressions always evaluates to false}}
res = ((int)f + 4 != (int)f + 4);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:21: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
int checkNotEqualBinaryOpIntCompare4(void) {
@@ -196,7 +202,8 @@ int checkNotEqualBinaryOpIntCompare5(void) {
int res;
int t= 1;
int u= 2;
res = (u + t != u + t); // expected-warning {{comparison of identical expressions always evaluates to false}}
res = (u + t != u + t);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:16: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
@@ -205,7 +212,8 @@ int checkNotEqualNestedBinaryOpIntCompare1(void) {
int t= 1;
int u= 2;
int f= 3;
res = (((int)f + (3 - u)*t) != ((int)f + (3 - u)*t)); // expected-warning {{comparison of identical expressions always evaluates to false}}
res = (((int)f + (3 - u)*t) != ((int)f + (3 - u)*t));
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:31: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
@@ -223,7 +231,8 @@ int checkNotEqualNestedBinaryOpIntCompare3(void) {
int t= 1;
int u= 2;
int f= 3;
res = (((int)f + (u - 3)*t) != ((int)f + (3 - u)*(t + 1 != t + 1))); // expected-warning {{comparison of identical expressions always evaluates to false}}
res = (((int)f + (u - 3)*t) != ((int)f + (3 - u)*(t + 1 != t + 1)));
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:59: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
@@ -252,7 +261,8 @@ int checkNotEqualIntPointerDeclCompare1(void) {
int checkNotEqualCastIntPointerDeclCompare11(void) {
int k = 7;
int* f = &k;
return ((int*)f != (int*)f); // expected-warning {{comparison of identical expressions always evaluates to false}}
return ((int*)f != (int*)f);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:19: warning: both sides of operator are equivalent [misc-redundant-expression]
}
int checkNotEqualCastIntPointerDeclCompare12(void) {
int k = 7;
@@ -263,7 +273,8 @@ int checkNotEqualBinaryOpIntPointerCompare1(void) {
int k = 7;
int res;
int* f= &k;
res = (f + 4 != f + 4); // expected-warning {{comparison of identical expressions always evaluates to false}}
res = (f + 4 != f + 4);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:16: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
int checkNotEqualBinaryOpIntPointerCompare2(void) {
@@ -278,7 +289,8 @@ int checkNotEqualBinaryOpIntPointerCompare3(void) {
int k = 7;
int res;
int* f= &k;
res = ((int*)f + 4 != (int*)f + 4); // expected-warning {{comparison of identical expressions always evaluates to false}}
res = ((int*)f + 4 != (int*)f + 4);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:22: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
int checkNotEqualBinaryOpIntPointerCompare4(void) {
@@ -295,7 +307,8 @@ int checkNotEqualNestedBinaryOpIntPointerCompare1(void) {
int t= 1;
int* u= &k+2;
int* f= &k+3;
res = ((f + (3)*t) != (f + (3)*t)); // expected-warning {{comparison of identical expressions always evaluates to false}}
res = ((f + (3)*t) != (f + (3)*t));
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:22: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
@@ -358,7 +371,8 @@ int checkEqualIntPointerDeclCompare(void) {
int checkEqualIntPointerDeclCompare0(void) {
int k = 3;
int* f = &k;
return (f+1 == f+1); // expected-warning {{comparison of identical expressions always evaluates to true}}
return (f+1 == f+1);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:15: warning: both sides of operator are equivalent [misc-redundant-expression]
}
/* EQ with float*/
@@ -410,7 +424,8 @@ int checkEqualFloatDeclCompare6(void) {
int checkEqualCastFloatDeclCompare11(void) {
float f = 7.1F;
return ((int)f == (int)f); // expected-warning {{comparison of identical expressions always evaluates to true}}
return ((int)f == (int)f);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:18: warning: both sides of operator are equivalent [misc-redundant-expression]
}
int checkEqualCastFloatDeclCompare12(void) {
float f = 7.1F;
@@ -474,7 +489,8 @@ int checkEqualNestedBinaryOpFloatCompare3(void) {
/* Equal with int*/
int checkEqualIntLiteralCompare1(void) {
return (5 == 5); // expected-warning {{comparison of identical expressions always evaluates to true}}
return (5 == 5);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:13: warning: both sides of operator are equivalent [misc-redundant-expression]
}
int checkEqualIntLiteralCompare2(void) {
@@ -489,7 +505,8 @@ int checkEqualIntDeclCompare1(void) {
int checkEqualCastIntDeclCompare11(void) {
int f = 7;
return ((int)f == (int)f); // expected-warning {{comparison of identical expressions always evaluates to true}}
return ((int)f == (int)f);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:18: warning: both sides of operator are equivalent [misc-redundant-expression]
}
int checkEqualCastIntDeclCompare12(void) {
int f = 7;
@@ -511,7 +528,8 @@ int checkEqualBinaryOpIntCompare1(void) {
int t= 1;
int u= 2;
int f= 4;
res = (f + 4 == f + 4); // expected-warning {{comparison of identical expressions always evaluates to true}}
res = (f + 4 == f + 4);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:16: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
int checkEqualBinaryOpIntCompare2(void) {
@@ -526,7 +544,8 @@ int checkEqualBinaryOpIntCompare3(void) {
int t= 1;
int u= 2;
int f= 4;
res = ((int)f + 4 == (int)f + 4); // expected-warning {{comparison of identical expressions always evaluates to true}}
res = ((int)f + 4 == (int)f + 4);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:21: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
@@ -542,7 +561,8 @@ int checkEqualBinaryOpIntCompare5(void) {
int res;
int t= 1;
int u= 2;
res = (u + t == u + t); // expected-warning {{comparison of identical expressions always evaluates to true}}
res = (u + t == u + t);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:16: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
@@ -551,7 +571,8 @@ int checkEqualNestedBinaryOpIntCompare1(void) {
int t= 1;
int u= 2;
int f= 3;
res = (((int)f + (3 - u)*t) == ((int)f + (3 - u)*t)); // expected-warning {{comparison of identical expressions always evaluates to true}}
res = (((int)f + (3 - u)*t) == ((int)f + (3 - u)*t));
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:31: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
@@ -569,7 +590,8 @@ int checkEqualNestedBinaryOpIntCompare3(void) {
int t= 1;
int u= 2;
int f= 3;
res = (((int)f + (u - 3)*t) == ((int)f + (3 - u)*(t + 1 == t + 1))); // expected-warning {{comparison of identical expressions always evaluates to true}}
res = (((int)f + (u - 3)*t) == ((int)f + (3 - u)*(t + 1 == t + 1)));
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:59: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
@@ -615,7 +637,8 @@ int checkEqualSameFunctionDifferentParam() {
/* LT with float */
int checkLessThanFloatLiteralCompare1(void) {
return (5.14F < 5.14F); // expected-warning {{comparison of identical expressions always evaluates to false}}
return (5.14F < 5.14F);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:17: warning: both sides of operator are equivalent [misc-redundant-expression]
}
int checkLessThanFloatLiteralCompare2(void) {
@@ -630,7 +653,8 @@ int checkLessThanFloatDeclCompare1(void) {
int checkLessThanFloatDeclCompare12(void) {
float f = 7.1F;
return (f < f); // expected-warning {{comparison of identical expressions always evaluates to false}}
return (f < f);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:13: warning: both sides of operator are equivalent [misc-redundant-expression]
}
int checkLessThanFloatDeclCompare3(void) {
@@ -658,7 +682,8 @@ int checkLessThanFloatDeclCompare6(void) {
int checkLessThanCastFloatDeclCompare11(void) {
float f = 7.1F;
return ((int)f < (int)f); // expected-warning {{comparison of identical expressions always evaluates to false}}
return ((int)f < (int)f);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:18: warning: both sides of operator are equivalent [misc-redundant-expression]
}
int checkLessThanCastFloatDeclCompare12(void) {
float f = 7.1F;
@@ -721,7 +746,8 @@ int checkLessThanNestedBinaryOpFloatCompare3(void) {
int checkLessThanIntLiteralCompare1(void) {
return (5 < 5); // expected-warning {{comparison of identical expressions always evaluates to false}}
return (5 < 5);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:13: warning: both sides of operator are equivalent [misc-redundant-expression]
}
int checkLessThanIntLiteralCompare2(void) {
@@ -758,7 +784,8 @@ int checkLessThanIntDeclCompare6(void) {
int checkLessThanCastIntDeclCompare11(void) {
int f = 7;
return ((int)f < (int)f); // expected-warning {{comparison of identical expressions always evaluates to false}}
return ((int)f < (int)f);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:18: warning: both sides of operator are equivalent [misc-redundant-expression]
}
int checkLessThanCastIntDeclCompare12(void) {
int f = 7;
@@ -767,7 +794,8 @@ int checkLessThanCastIntDeclCompare12(void) {
int checkLessThanBinaryOpIntCompare1(void) {
int res;
int f= 3;
res = (f + 3 < f + 3); // expected-warning {{comparison of identical expressions always evaluates to false}}
res = (f + 3 < f + 3);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:16: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
int checkLessThanBinaryOpIntCompare2(void) {
@@ -778,7 +806,8 @@ int checkLessThanBinaryOpIntCompare2(void) {
int checkLessThanBinaryOpIntCompare3(void) {
int res;
int f= 3;
res = ((int)f + 3 < (int)f + 3); // expected-warning {{comparison of identical expressions always evaluates to false}}
res = ((int)f + 3 < (int)f + 3);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:21: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
int checkLessThanBinaryOpIntCompare4(void) {
@@ -793,7 +822,8 @@ int checkLessThanNestedBinaryOpIntCompare1(void) {
int t= 1;
int u= 2;
int f= 3;
res = (((int)f + (3 - u)*t) < ((int)f + (3 - u)*t)); // expected-warning {{comparison of identical expressions always evaluates to false}}
res = (((int)f + (3 - u)*t) < ((int)f + (3 - u)*t));
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:31: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
@@ -811,7 +841,8 @@ int checkLessThanNestedBinaryOpIntCompare3(void) {
int t= 1;
int u= 2;
int f= 3;
res = (((int)f + (u - 3)*t) < ((int)f + (3 - u)*(t + u < t + u))); // expected-warning {{comparison of identical expressions always evaluates to false}}
res = (((int)f + (u - 3)*t) < ((int)f + (3 - u)*(t + u < t + u)));
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:58: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
@@ -825,7 +856,8 @@ int checkLessThanNestedBinaryOpIntCompare3(void) {
/* GT with float */
int checkGreaterThanFloatLiteralCompare1(void) {
return (5.14F > 5.14F); // expected-warning {{comparison of identical expressions always evaluates to false}}
return (5.14F > 5.14F);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:17: warning: both sides of operator are equivalent [misc-redundant-expression]
}
int checkGreaterThanFloatLiteralCompare2(void) {
@@ -841,7 +873,8 @@ int checkGreaterThanFloatDeclCompare1(void) {
int checkGreaterThanFloatDeclCompare12(void) {
float f = 7.1F;
return (f > f); // expected-warning {{comparison of identical expressions always evaluates to false}}
return (f > f);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:13: warning: both sides of operator are equivalent [misc-redundant-expression]
}
@@ -869,7 +902,8 @@ int checkGreaterThanFloatDeclCompare6(void) {
int checkGreaterThanCastFloatDeclCompare11(void) {
float f = 7.1F;
return ((int)f > (int)f); // expected-warning {{comparison of identical expressions always evaluates to false}}
return ((int)f > (int)f);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:18: warning: both sides of operator are equivalent [misc-redundant-expression]
}
int checkGreaterThanCastFloatDeclCompare12(void) {
float f = 7.1F;
@@ -932,7 +966,8 @@ int checkGreaterThanNestedBinaryOpFloatCompare3(void) {
int checkGreaterThanIntLiteralCompare1(void) {
return (5 > 5); // expected-warning {{comparison of identical expressions always evaluates to false}}
return (5 > 5);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:13: warning: both sides of operator are equivalent [misc-redundant-expression]
}
int checkGreaterThanIntLiteralCompare2(void) {
@@ -958,7 +993,8 @@ int checkGreaterThanIntDeclCompare4(void) {
int checkGreaterThanCastIntDeclCompare11(void) {
int f = 7;
return ((int)f > (int)f); // expected-warning {{comparison of identical expressions always evaluates to false}}
return ((int)f > (int)f);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:18: warning: both sides of operator are equivalent [misc-redundant-expression]
}
int checkGreaterThanCastIntDeclCompare12(void) {
int f = 7;
@@ -967,7 +1003,8 @@ int checkGreaterThanCastIntDeclCompare12(void) {
int checkGreaterThanBinaryOpIntCompare1(void) {
int res;
int f= 3;
res = (f + 3 > f + 3); // expected-warning {{comparison of identical expressions always evaluates to false}}
res = (f + 3 > f + 3);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:16: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
int checkGreaterThanBinaryOpIntCompare2(void) {
@@ -978,7 +1015,8 @@ int checkGreaterThanBinaryOpIntCompare2(void) {
int checkGreaterThanBinaryOpIntCompare3(void) {
int res;
int f= 3;
res = ((int)f + 3 > (int)f + 3); // expected-warning {{comparison of identical expressions always evaluates to false}}
res = ((int)f + 3 > (int)f + 3);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:21: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
int checkGreaterThanBinaryOpIntCompare4(void) {
@@ -993,7 +1031,8 @@ int checkGreaterThanNestedBinaryOpIntCompare1(void) {
int t= 1;
int u= 2;
int f= 3;
res = (((int)f + (3 - u)*t) > ((int)f + (3 - u)*t)); // expected-warning {{comparison of identical expressions always evaluates to false}}
res = (((int)f + (3 - u)*t) > ((int)f + (3 - u)*t));
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:31: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
@@ -1011,7 +1050,8 @@ int checkGreaterThanNestedBinaryOpIntCompare3(void) {
int t= 1;
int u= 2;
int f= 3;
res = (((int)f + (u - 3)*t) > ((int)f + (3 - u)*(t + u > t + u))); // expected-warning {{comparison of identical expressions always evaluates to false}}
res = (((int)f + (u - 3)*t) > ((int)f + (3 - u)*(t + u > t + u)));
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:58: warning: both sides of operator are equivalent [misc-redundant-expression]
return (0);
}
@@ -1024,45 +1064,61 @@ int checkGreaterThanNestedBinaryOpIntCompare3(void) {
unsigned test_unsigned(unsigned a) {
unsigned b = 1;
a = a > 5 ? b : b; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
a = a > 5 ? b : b;
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:17: warning: 'true' and 'false' expressions are equivalent [misc-redundant-expression]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:13: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
return a;
}
void test_signed() {
int a = 0;
a = a > 5 ? a : a; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
a = a > 5 ? a : a;
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:17: warning: 'true' and 'false' expressions are equivalent [misc-redundant-expression]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:13: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
}
void test_bool(bool a) {
a = a > 0 ? a : a; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
a = a > 0 ? a : a;
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:17: warning: 'true' and 'false' expressions are equivalent [misc-redundant-expression]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:13: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
}
void test_float() {
float a = 0;
float b = 0;
a = a > 5 ? a : a; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
a = a > 5 ? a : a;
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:17: warning: 'true' and 'false' expressions are equivalent [misc-redundant-expression]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:13: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
}
const char *test_string() {
float a = 0;
return a > 5 ? "abc" : "abc"; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
return a > 5 ? "abc" : "abc";
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:24: warning: 'true' and 'false' expressions are equivalent [misc-redundant-expression]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:16: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
}
void test_unsigned_expr() {
unsigned a = 0;
unsigned b = 0;
a = a > 5 ? a+b : a+b; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
a = a > 5 ? a+b : a+b;
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:19: warning: 'true' and 'false' expressions are equivalent [misc-redundant-expression]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:13: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
}
void test_signed_expr() {
int a = 0;
int b = 1;
a = a > 5 ? a+b : a+b; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
a = a > 5 ? a+b : a+b;
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:19: warning: 'true' and 'false' expressions are equivalent [misc-redundant-expression]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:13: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
}
void test_bool_expr(bool a) {
bool b = 0;
a = a > 0 ? a&&b : a&&b; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
a = a > 0 ? a&&b : a&&b;
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:20: warning: 'true' and 'false' expressions are equivalent [misc-redundant-expression]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:13: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
}
void test_unsigned_expr_negative() {
@@ -1085,13 +1141,17 @@ void test_bool_expr_negative(bool a) {
void test_float_expr_positive() {
float a = 0;
float b = 0;
a = a > 5 ? a+b : a+b; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
a = a > 5 ? a+b : a+b;
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:19: warning: 'true' and 'false' expressions are equivalent [misc-redundant-expression]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:13: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
}
void test_expr_positive_func() {
unsigned a = 0;
unsigned b = 1;
a = a > 5 ? a+func() : a+func(); // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
a = a > 5 ? a+func() : a+func();
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:24: warning: 'true' and 'false' expressions are equivalent [misc-redundant-expression]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:13: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
}
void test_expr_negative_func() {
@@ -1103,7 +1163,9 @@ void test_expr_negative_func() {
void test_expr_positive_funcParam() {
unsigned a = 0;
unsigned b = 1;
a = a > 5 ? a+funcParam(b) : a+funcParam(b); // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
a = a > 5 ? a+funcParam(b) : a+funcParam(b);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:30: warning: 'true' and 'false' expressions are equivalent [misc-redundant-expression]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:13: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
}
void test_expr_negative_funcParam() {
@@ -1115,7 +1177,8 @@ void test_expr_negative_funcParam() {
void test_expr_positive_inc() {
unsigned a = 0;
unsigned b = 1;
a = a > 5 ? a++ : a++; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
a = a > 5 ? a++ : a++;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:13: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
}
void test_expr_negative_inc() {
@@ -1127,7 +1190,8 @@ void test_expr_negative_inc() {
void test_expr_positive_assign() {
unsigned a = 0;
unsigned b = 1;
a = a > 5 ? a=1 : a=1; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
a = a > 5 ? a=1 : a=1;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:13: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
}
void test_expr_negative_assign() {
@@ -1140,7 +1204,9 @@ void test_signed_nested_expr() {
int a = 0;
int b = 1;
int c = 3;
a = a > 5 ? a+b+(c+a)*(a + b*(c+a)) : a+b+(c+a)*(a + b*(c+a)); // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
a = a > 5 ? a+b+(c+a)*(a + b*(c+a)) : a+b+(c+a)*(a + b*(c+a));
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:39: warning: 'true' and 'false' expressions are equivalent [misc-redundant-expression]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:13: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
}
void test_signed_nested_expr_negative() {
@@ -1161,23 +1227,29 @@ void test_signed_nested_cond_expr() {
int a = 0;
int b = 1;
int c = 3;
a = a > 5 ? (b > 5 ? 1 : 4) : (b > 5 ? 4 : 4); // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
a = a > 5 ? (b > 5 ? 1 : 4) : (b > 5 ? 4 : 4);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:44: warning: 'true' and 'false' expressions are equivalent [misc-redundant-expression]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:40: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
}
void test_identical_branches1(bool b) {
int i = 0;
if (b) { // expected-warning {{true and false branches are identical}}
if (b) {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
++i;
} else {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: else branch starts here
++i;
}
}
void test_identical_branches2(bool b) {
int i = 0;
if (b) { // expected-warning {{true and false branches are identical}}
if (b) {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
++i;
} else
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: else branch starts here
++i;
}
@@ -1192,33 +1264,41 @@ void test_identical_branches3(bool b) {
void test_identical_branches4(bool b) {
int i = 0;
if (b) { // expected-warning {{true and false branches are identical}}
if (b) {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
} else {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: else branch starts here
}
}
void test_identical_branches_break(bool b) {
while (true) {
if (b) // expected-warning {{true and false branches are identical}}
if (b)
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: warning: if with identical then and else branches [bugprone-branch-clone]
break;
else
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: else branch starts here
break;
}
}
void test_identical_branches_continue(bool b) {
while (true) {
if (b) // expected-warning {{true and false branches are identical}}
if (b)
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: warning: if with identical then and else branches [bugprone-branch-clone]
continue;
else
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: else branch starts here
continue;
}
}
void test_identical_branches_func(bool b) {
if (b) // expected-warning {{true and false branches are identical}}
if (b)
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
func();
else
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:3: note: else branch starts here
func();
}
@@ -1239,27 +1319,33 @@ void test_identical_branches_cast1(bool b) {
void test_identical_branches_cast2(bool b) {
long v = -7;
if (b) // expected-warning {{true and false branches are identical}}
if (b)
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
v = (signed int) v;
else
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:3: note: else branch starts here
v = (signed int) v;
}
int test_identical_branches_return_int(bool b) {
int i = 0;
if (b) { // expected-warning {{true and false branches are identical}}
if (b) {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
i++;
return i;
} else {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: else branch starts here
i++;
return i;
}
}
int test_identical_branches_return_func(bool b) {
if (b) { // expected-warning {{true and false branches are identical}}
if (b) {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
return func();
} else {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: else branch starts here
return func();
}
}
@@ -1267,10 +1353,12 @@ int test_identical_branches_return_func(bool b) {
void test_identical_branches_for(bool b) {
int i;
int j;
if (b) { // expected-warning {{true and false branches are identical}}
if (b) {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
for (i = 0, j = 0; i < 10; i++)
j += 4;
} else {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: else branch starts here
for (i = 0, j = 0; i < 10; i++)
j += 4;
}
@@ -1278,10 +1366,12 @@ void test_identical_branches_for(bool b) {
void test_identical_branches_while(bool b) {
int i = 10;
if (b) { // expected-warning {{true and false branches are identical}}
if (b) {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
while (func())
i--;
} else {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: else branch starts here
while (func())
i--;
}
@@ -1300,11 +1390,13 @@ void test_identical_branches_while_2(bool b) {
void test_identical_branches_do_while(bool b) {
int i = 10;
if (b) { // expected-warning {{true and false branches are identical}}
if (b) {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
do {
i--;
} while (func());
} else {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: else branch starts here
do {
i--;
} while (func());
@@ -1312,27 +1404,32 @@ void test_identical_branches_do_while(bool b) {
}
void test_identical_branches_if(bool b, int i) {
if (b) { // expected-warning {{true and false branches are identical}}
if (b) {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:3: warning: if with identical then and else branches [bugprone-branch-clone]
if (i < 5)
i += 10;
} else {
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: else branch starts here
if (i < 5)
i += 10;
}
}
void test_identical_bitwise1() {
int a = 5 | 5; // expected-warning {{identical expressions on both sides of bitwise operator}}
int a = 5 | 5;
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:13: warning: both sides of operator are equivalent [misc-redundant-expression]
}
void test_identical_bitwise2() {
int a = 5;
int b = a | a; // expected-warning {{identical expressions on both sides of bitwise operator}}
int b = a | a;
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:13: warning: both sides of operator are equivalent [misc-redundant-expression]
}
void test_identical_bitwise3() {
int a = 5;
int b = (a | a); // expected-warning {{identical expressions on both sides of bitwise operator}}
int b = (a | a);
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:14: warning: both sides of operator are equivalent [misc-redundant-expression]
}
void test_identical_bitwise4() {
@@ -1348,21 +1445,25 @@ void test_identical_bitwise5() {
void test_identical_bitwise6() {
int a = 5;
int b = a | 4 | a; // expected-warning {{identical expressions on both sides of bitwise operator}}
int b = a | 4 | a;
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:17: warning: operator has equivalent nested operands [misc-redundant-expression]
}
void test_identical_bitwise7() {
int a = 5;
int b = func() | func(); // no-warning
int b = func() | func();
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:18: warning: both sides of operator are equivalent [misc-redundant-expression]
}
void test_identical_logical1(int a) {
if (a == 4 && a == 4) // expected-warning {{identical expressions on both sides of logical operator}}
if (a == 4 && a == 4)
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:14: warning: both sides of operator are equivalent [misc-redundant-expression]
;
}
void test_identical_logical2(int a) {
if (a == 4 || a == 5 || a == 4) // expected-warning {{identical expressions on both sides of logical operator}}
if (a == 4 || a == 5 || a == 4)
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:24: warning: operator has equivalent nested operands [misc-redundant-expression]
;
}
@@ -1384,7 +1485,8 @@ void test_identical_logical5(int x, int y) {
}
void test_identical_logical6(int x, int y) {
if (x == 4 && y == 5 || x == 4 && y == 5) // expected-warning {{identical expressions on both sides of logical operator}}
if (x == 4 && y == 5 || x == 4 && y == 5)
// CHECK-MESSAGES-IDENTEXPR: :[[@LINE-1]]:24: warning: both sides of operator are equivalent [misc-redundant-expression]
;
}
@@ -1410,51 +1512,73 @@ void test_identical_logical9(int x, int y) {
void test_warn_chained_if_stmts_1(int x) {
if (x == 1)
;
else if (x == 1) // expected-warning {{expression is identical to previous condition}}
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: warning: repeated branch body in conditional chain [bugprone-branch-clone]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:6: note: end of the original
else if (x == 1)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 1 starts here
}
void test_warn_chained_if_stmts_2(int x) {
if (x == 1)
;
else if (x == 1) // expected-warning {{expression is identical to previous condition}}
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: warning: repeated branch body in conditional chain [bugprone-branch-clone]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:6: note: end of the original
else if (x == 1)
;
else if (x == 1) // expected-warning {{expression is identical to previous condition}}
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 1 starts here
else if (x == 1)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 2 starts here
}
void test_warn_chained_if_stmts_3(int x) {
if (x == 1)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: warning: repeated branch body in conditional chain [bugprone-branch-clone]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:6: note: end of the original
else if (x == 2)
;
else if (x == 1) // expected-warning {{expression is identical to previous condition}}
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 1 starts here
else if (x == 1)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 2 starts here
}
void test_warn_chained_if_stmts_4(int x) {
if (x == 1)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: warning: repeated branch body in conditional chain [bugprone-branch-clone]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:6: note: end of the original
else if (func())
;
else if (x == 1) // expected-warning {{expression is identical to previous condition}}
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 1 starts here
else if (x == 1)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 2 starts here
}
void test_warn_chained_if_stmts_5(int x) {
if (x & 1)
;
else if (x & 1) // expected-warning {{expression is identical to previous condition}}
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: warning: repeated branch body in conditional chain [bugprone-branch-clone]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:6: note: end of the original
else if (x & 1)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 1 starts here
}
void test_warn_chained_if_stmts_6(int x) {
if (x == 1)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: warning: repeated branch body in conditional chain [bugprone-branch-clone]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:6: note: end of the original
else if (x == 2)
;
else if (x == 2) // expected-warning {{expression is identical to previous condition}}
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 1 starts here
else if (x == 2)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 2 starts here
else if (x == 3)
;
}
@@ -1462,58 +1586,83 @@ void test_warn_chained_if_stmts_6(int x) {
void test_warn_chained_if_stmts_7(int x) {
if (x == 1)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: warning: repeated branch body in conditional chain [bugprone-branch-clone]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:6: note: end of the original
else if (x == 2)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 1 starts here
else if (x == 3)
;
else if (x == 2) // expected-warning {{expression is identical to previous condition}}
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 2 starts here
else if (x == 2)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 3 starts here
else if (x == 5)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 4 starts here
}
void test_warn_chained_if_stmts_8(int x) {
if (x == 1)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: warning: repeated branch body in conditional chain [bugprone-branch-clone]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:6: note: end of the original
else if (x == 2)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 1 starts here
else if (x == 3)
;
else if (x == 2) // expected-warning {{expression is identical to previous condition}}
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 2 starts here
else if (x == 2)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 3 starts here
else if (x == 5)
;
else if (x == 3) // expected-warning {{expression is identical to previous condition}}
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 4 starts here
else if (x == 3)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 5 starts here
else if (x == 7)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 6 starts here
}
void test_nowarn_chained_if_stmts_1(int x) {
if (func())
;
else if (func()) // no-warning
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: warning: repeated branch body in conditional chain [bugprone-branch-clone]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:6: note: end of the original
else if (func())
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 1 starts here
}
void test_nowarn_chained_if_stmts_2(int x) {
if (func())
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: warning: repeated branch body in conditional chain [bugprone-branch-clone]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:6: note: end of the original
else if (x == 1)
;
else if (func()) // no-warning
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 1 starts here
else if (func())
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 2 starts here
}
void test_nowarn_chained_if_stmts_3(int x) {
if (x++)
;
else if (x++) // no-warning
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: warning: repeated branch body in conditional chain [bugprone-branch-clone]
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-2]]:6: note: end of the original
else if (x++)
;
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: clone 1 starts here
}
void test_warn_wchar() {
const wchar_t * a = 0 ? L"Warning" : L"Warning"; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
const wchar_t * a = 0 ? L"Warning" : L"Warning";
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:25: warning: conditional operator with identical true and false expressions [bugprone-branch-clone]
}
void test_nowarn_wchar() {
const wchar_t * a = 0 ? L"No" : L"Warning";
@@ -1525,7 +1674,7 @@ void test_nowarn_long() {
if (0) {
b -= a;
c = 0;
} else { // no-warning
} else {
b -= a;
c = 0LL;
}
@@ -1535,7 +1684,9 @@ void test_nowarn_long() {
void test_warn_inner_if_1(int x) {
if (x == 1) {
if (x == 1) // expected-warning {{conditions of the inner and outer statements are identical}}
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:3: warning: if with identical inner if statement [bugprone-branch-clone]
if (x == 1)
// CHECK-MESSAGES-BUGPRONEBRANCH: :[[@LINE-1]]:5: note: inner if starts here
;
}

View File

@@ -1016,6 +1016,10 @@ Improvements
Moved checkers
^^^^^^^^^^^^^^
- The checker ``alpha.core.IdenticalExpr`` was deleted because it was
duplicated in the clang-tidy checkers ``misc-redundant-expression`` and
``bugprone-branch-clone``.
- The checker ``alpha.security.MallocOverflow`` was deleted because it was
badly implemented and its agressive logic produced too many false positives.
To detect too large arguments passed to malloc, consider using the checker

View File

@@ -2842,36 +2842,6 @@ Check for assignment of a fixed address to a pointer.
p = (int *) 0x10000; // warn
}
.. _alpha-core-IdenticalExpr:
alpha.core.IdenticalExpr (C, C++)
"""""""""""""""""""""""""""""""""
Warn about unintended use of identical expressions in operators.
.. code-block:: cpp
// C
void test() {
int a = 5;
int b = a | 4 | a; // warn: identical expr on both sides
}
// C++
bool f(void);
void test(bool b) {
int i = 10;
if (f()) { // warn: true and false branches are identical
do {
i--;
} while (f());
} else {
do {
i--;
} while (f());
}
}
.. _alpha-core-PointerArithm:
alpha.core.PointerArithm (C)

View File

@@ -275,10 +275,6 @@ def ConversionChecker : Checker<"Conversion">,
HelpText<"Loss of sign/precision in implicit conversions">,
Documentation<HasDocumentation>;
def IdenticalExprChecker : Checker<"IdenticalExpr">,
HelpText<"Warn about unintended use of identical expressions in operators">,
Documentation<HasDocumentation>;
def FixedAddressChecker : Checker<"FixedAddr">,
HelpText<"Check for assignment of a fixed address to a pointer">,
Documentation<HasDocumentation>;

View File

@@ -50,7 +50,6 @@ add_clang_library(clangStaticAnalyzerCheckers
GCDAntipatternChecker.cpp
GenericTaintChecker.cpp
GTestChecker.cpp
IdenticalExprChecker.cpp
InnerPointerChecker.cpp
InvalidatedIteratorChecker.cpp
cert/InvalidPtrChecker.cpp

View File

@@ -1,519 +0,0 @@
//== IdenticalExprChecker.cpp - Identical expression checker----------------==//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This defines IdenticalExprChecker, a check that warns about
/// unintended use of identical expressions.
///
/// It checks for use of identical expressions with comparison operators and
/// inside conditional expressions.
///
//===----------------------------------------------------------------------===//
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
using namespace ento;
static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
const Stmt *Stmt2, bool IgnoreSideEffects = false);
//===----------------------------------------------------------------------===//
// FindIdenticalExprVisitor - Identify nodes using identical expressions.
//===----------------------------------------------------------------------===//
namespace {
class FindIdenticalExprVisitor : public DynamicRecursiveASTVisitor {
BugReporter &BR;
const CheckerBase *Checker;
AnalysisDeclContext *AC;
public:
explicit FindIdenticalExprVisitor(BugReporter &B,
const CheckerBase *Checker,
AnalysisDeclContext *A)
: BR(B), Checker(Checker), AC(A) {}
// FindIdenticalExprVisitor only visits nodes
// that are binary operators, if statements or
// conditional operators.
bool VisitBinaryOperator(BinaryOperator *B) override;
bool VisitIfStmt(IfStmt *I) override;
bool VisitConditionalOperator(ConditionalOperator *C) override;
private:
void reportIdenticalExpr(const BinaryOperator *B, bool CheckBitwise,
ArrayRef<SourceRange> Sr);
void checkBitwiseOrLogicalOp(const BinaryOperator *B, bool CheckBitwise);
void checkComparisonOp(const BinaryOperator *B);
};
} // end anonymous namespace
void FindIdenticalExprVisitor::reportIdenticalExpr(const BinaryOperator *B,
bool CheckBitwise,
ArrayRef<SourceRange> Sr) {
StringRef Message;
if (CheckBitwise)
Message = "identical expressions on both sides of bitwise operator";
else
Message = "identical expressions on both sides of logical operator";
PathDiagnosticLocation ELoc =
PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
BR.EmitBasicReport(AC->getDecl(), Checker,
"Use of identical expressions",
categories::LogicError,
Message, ELoc, Sr);
}
void FindIdenticalExprVisitor::checkBitwiseOrLogicalOp(const BinaryOperator *B,
bool CheckBitwise) {
SourceRange Sr[2];
const Expr *LHS = B->getLHS();
const Expr *RHS = B->getRHS();
// Split operators as long as we still have operators to split on. We will
// get called for every binary operator in an expression so there is no need
// to check every one against each other here, just the right most one with
// the others.
while (const BinaryOperator *B2 = dyn_cast<BinaryOperator>(LHS)) {
if (B->getOpcode() != B2->getOpcode())
break;
if (isIdenticalStmt(AC->getASTContext(), RHS, B2->getRHS())) {
Sr[0] = RHS->getSourceRange();
Sr[1] = B2->getRHS()->getSourceRange();
reportIdenticalExpr(B, CheckBitwise, Sr);
}
LHS = B2->getLHS();
}
if (isIdenticalStmt(AC->getASTContext(), RHS, LHS)) {
Sr[0] = RHS->getSourceRange();
Sr[1] = LHS->getSourceRange();
reportIdenticalExpr(B, CheckBitwise, Sr);
}
}
bool FindIdenticalExprVisitor::VisitIfStmt(IfStmt *I) {
const Stmt *Stmt1 = I->getThen();
const Stmt *Stmt2 = I->getElse();
// Check for identical inner condition:
//
// if (x<10) {
// if (x<10) {
// ..
if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(Stmt1)) {
if (!CS->body_empty()) {
const IfStmt *InnerIf = dyn_cast<IfStmt>(*CS->body_begin());
if (InnerIf && isIdenticalStmt(AC->getASTContext(), I->getCond(), InnerIf->getCond(), /*IgnoreSideEffects=*/ false)) {
PathDiagnosticLocation ELoc(InnerIf->getCond(), BR.getSourceManager(), AC);
BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions",
categories::LogicError,
"conditions of the inner and outer statements are identical",
ELoc);
}
}
}
// Check for identical conditions:
//
// if (b) {
// foo1();
// } else if (b) {
// foo2();
// }
if (Stmt1 && Stmt2) {
const Expr *Cond1 = I->getCond();
const Stmt *Else = Stmt2;
while (const IfStmt *I2 = dyn_cast_or_null<IfStmt>(Else)) {
const Expr *Cond2 = I2->getCond();
if (isIdenticalStmt(AC->getASTContext(), Cond1, Cond2, false)) {
SourceRange Sr = Cond1->getSourceRange();
PathDiagnosticLocation ELoc(Cond2, BR.getSourceManager(), AC);
BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions",
categories::LogicError,
"expression is identical to previous condition",
ELoc, Sr);
}
Else = I2->getElse();
}
}
if (!Stmt1 || !Stmt2)
return true;
// Special handling for code like:
//
// if (b) {
// i = 1;
// } else
// i = 1;
if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt1)) {
if (CompStmt->size() == 1)
Stmt1 = CompStmt->body_back();
}
if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt2)) {
if (CompStmt->size() == 1)
Stmt2 = CompStmt->body_back();
}
if (isIdenticalStmt(AC->getASTContext(), Stmt1, Stmt2, true)) {
PathDiagnosticLocation ELoc =
PathDiagnosticLocation::createBegin(I, BR.getSourceManager(), AC);
BR.EmitBasicReport(AC->getDecl(), Checker,
"Identical branches",
categories::LogicError,
"true and false branches are identical", ELoc);
}
return true;
}
bool FindIdenticalExprVisitor::VisitBinaryOperator(BinaryOperator *B) {
BinaryOperator::Opcode Op = B->getOpcode();
if (BinaryOperator::isBitwiseOp(Op))
checkBitwiseOrLogicalOp(B, true);
if (BinaryOperator::isLogicalOp(Op))
checkBitwiseOrLogicalOp(B, false);
if (BinaryOperator::isComparisonOp(Op))
checkComparisonOp(B);
// We want to visit ALL nodes (subexpressions of binary comparison
// expressions too) that contains comparison operators.
// True is always returned to traverse ALL nodes.
return true;
}
void FindIdenticalExprVisitor::checkComparisonOp(const BinaryOperator *B) {
BinaryOperator::Opcode Op = B->getOpcode();
//
// Special case for floating-point representation.
//
// If expressions on both sides of comparison operator are of type float,
// then for some comparison operators no warning shall be
// reported even if the expressions are identical from a symbolic point of
// view. Comparison between expressions, declared variables and literals
// are treated differently.
//
// != and == between float literals that have the same value should NOT warn.
// < > between float literals that have the same value SHOULD warn.
//
// != and == between the same float declaration should NOT warn.
// < > between the same float declaration SHOULD warn.
//
// != and == between eq. expressions that evaluates into float
// should NOT warn.
// < > between eq. expressions that evaluates into float
// should NOT warn.
//
const Expr *LHS = B->getLHS()->IgnoreParenImpCasts();
const Expr *RHS = B->getRHS()->IgnoreParenImpCasts();
const DeclRefExpr *DeclRef1 = dyn_cast<DeclRefExpr>(LHS);
const DeclRefExpr *DeclRef2 = dyn_cast<DeclRefExpr>(RHS);
const FloatingLiteral *FloatLit1 = dyn_cast<FloatingLiteral>(LHS);
const FloatingLiteral *FloatLit2 = dyn_cast<FloatingLiteral>(RHS);
if ((DeclRef1) && (DeclRef2)) {
if ((DeclRef1->getType()->hasFloatingRepresentation()) &&
(DeclRef2->getType()->hasFloatingRepresentation())) {
if (DeclRef1->getDecl() == DeclRef2->getDecl()) {
if ((Op == BO_EQ) || (Op == BO_NE)) {
return;
}
}
}
} else if ((FloatLit1) && (FloatLit2)) {
if (FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue())) {
if ((Op == BO_EQ) || (Op == BO_NE)) {
return;
}
}
} else if (LHS->getType()->hasFloatingRepresentation()) {
// If any side of comparison operator still has floating-point
// representation, then it's an expression. Don't warn.
// Here only LHS is checked since RHS will be implicit casted to float.
return;
} else {
// No special case with floating-point representation, report as usual.
}
if (isIdenticalStmt(AC->getASTContext(), B->getLHS(), B->getRHS())) {
PathDiagnosticLocation ELoc =
PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
StringRef Message;
if (Op == BO_Cmp)
Message = "comparison of identical expressions always evaluates to "
"'equal'";
else if (((Op == BO_EQ) || (Op == BO_LE) || (Op == BO_GE)))
Message = "comparison of identical expressions always evaluates to true";
else
Message = "comparison of identical expressions always evaluates to false";
BR.EmitBasicReport(AC->getDecl(), Checker,
"Compare of identical expressions",
categories::LogicError, Message, ELoc);
}
}
bool FindIdenticalExprVisitor::VisitConditionalOperator(
ConditionalOperator *C) {
// Check if expressions in conditional expression are identical
// from a symbolic point of view.
if (isIdenticalStmt(AC->getASTContext(), C->getTrueExpr(),
C->getFalseExpr(), true)) {
PathDiagnosticLocation ELoc =
PathDiagnosticLocation::createConditionalColonLoc(
C, BR.getSourceManager());
SourceRange Sr[2];
Sr[0] = C->getTrueExpr()->getSourceRange();
Sr[1] = C->getFalseExpr()->getSourceRange();
BR.EmitBasicReport(
AC->getDecl(), Checker,
"Identical expressions in conditional expression",
categories::LogicError,
"identical expressions on both sides of ':' in conditional expression",
ELoc, Sr);
}
// We want to visit ALL nodes (expressions in conditional
// expressions too) that contains conditional operators,
// thus always return true to traverse ALL nodes.
return true;
}
/// Determines whether two statement trees are identical regarding
/// operators and symbols.
///
/// Exceptions: expressions containing macros or functions with possible side
/// effects are never considered identical.
/// Limitations: (t + u) and (u + t) are not considered identical.
/// t*(u + t) and t*u + t*t are not considered identical.
///
static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
const Stmt *Stmt2, bool IgnoreSideEffects) {
if (!Stmt1 || !Stmt2) {
return !Stmt1 && !Stmt2;
}
// If Stmt1 & Stmt2 are of different class then they are not
// identical statements.
if (Stmt1->getStmtClass() != Stmt2->getStmtClass())
return false;
const Expr *Expr1 = dyn_cast<Expr>(Stmt1);
const Expr *Expr2 = dyn_cast<Expr>(Stmt2);
if (Expr1 && Expr2) {
// If Stmt1 has side effects then don't warn even if expressions
// are identical.
if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx))
return false;
// If either expression comes from a macro then don't warn even if
// the expressions are identical.
if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID()))
return false;
// If all children of two expressions are identical, return true.
Expr::const_child_iterator I1 = Expr1->child_begin();
Expr::const_child_iterator I2 = Expr2->child_begin();
while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) {
if (!*I1 || !*I2 || !isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))
return false;
++I1;
++I2;
}
// If there are different number of children in the statements, return
// false.
if (I1 != Expr1->child_end())
return false;
if (I2 != Expr2->child_end())
return false;
}
switch (Stmt1->getStmtClass()) {
default:
return false;
case Stmt::CallExprClass:
case Stmt::ArraySubscriptExprClass:
case Stmt::ArraySectionExprClass:
case Stmt::OMPArrayShapingExprClass:
case Stmt::OMPIteratorExprClass:
case Stmt::ImplicitCastExprClass:
case Stmt::ParenExprClass:
case Stmt::BreakStmtClass:
case Stmt::ContinueStmtClass:
case Stmt::NullStmtClass:
return true;
case Stmt::CStyleCastExprClass: {
const CStyleCastExpr* CastExpr1 = cast<CStyleCastExpr>(Stmt1);
const CStyleCastExpr* CastExpr2 = cast<CStyleCastExpr>(Stmt2);
return CastExpr1->getTypeAsWritten() == CastExpr2->getTypeAsWritten();
}
case Stmt::ReturnStmtClass: {
const ReturnStmt *ReturnStmt1 = cast<ReturnStmt>(Stmt1);
const ReturnStmt *ReturnStmt2 = cast<ReturnStmt>(Stmt2);
return isIdenticalStmt(Ctx, ReturnStmt1->getRetValue(),
ReturnStmt2->getRetValue(), IgnoreSideEffects);
}
case Stmt::ForStmtClass: {
const ForStmt *ForStmt1 = cast<ForStmt>(Stmt1);
const ForStmt *ForStmt2 = cast<ForStmt>(Stmt2);
if (!isIdenticalStmt(Ctx, ForStmt1->getInit(), ForStmt2->getInit(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, ForStmt1->getCond(), ForStmt2->getCond(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, ForStmt1->getInc(), ForStmt2->getInc(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, ForStmt1->getBody(), ForStmt2->getBody(),
IgnoreSideEffects))
return false;
return true;
}
case Stmt::DoStmtClass: {
const DoStmt *DStmt1 = cast<DoStmt>(Stmt1);
const DoStmt *DStmt2 = cast<DoStmt>(Stmt2);
if (!isIdenticalStmt(Ctx, DStmt1->getCond(), DStmt2->getCond(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, DStmt1->getBody(), DStmt2->getBody(),
IgnoreSideEffects))
return false;
return true;
}
case Stmt::WhileStmtClass: {
const WhileStmt *WStmt1 = cast<WhileStmt>(Stmt1);
const WhileStmt *WStmt2 = cast<WhileStmt>(Stmt2);
if (!isIdenticalStmt(Ctx, WStmt1->getCond(), WStmt2->getCond(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, WStmt1->getBody(), WStmt2->getBody(),
IgnoreSideEffects))
return false;
return true;
}
case Stmt::IfStmtClass: {
const IfStmt *IStmt1 = cast<IfStmt>(Stmt1);
const IfStmt *IStmt2 = cast<IfStmt>(Stmt2);
if (!isIdenticalStmt(Ctx, IStmt1->getCond(), IStmt2->getCond(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, IStmt1->getThen(), IStmt2->getThen(),
IgnoreSideEffects))
return false;
if (!isIdenticalStmt(Ctx, IStmt1->getElse(), IStmt2->getElse(),
IgnoreSideEffects))
return false;
return true;
}
case Stmt::CompoundStmtClass: {
const CompoundStmt *CompStmt1 = cast<CompoundStmt>(Stmt1);
const CompoundStmt *CompStmt2 = cast<CompoundStmt>(Stmt2);
if (CompStmt1->size() != CompStmt2->size())
return false;
CompoundStmt::const_body_iterator I1 = CompStmt1->body_begin();
CompoundStmt::const_body_iterator I2 = CompStmt2->body_begin();
while (I1 != CompStmt1->body_end() && I2 != CompStmt2->body_end()) {
if (!isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))
return false;
++I1;
++I2;
}
return true;
}
case Stmt::CompoundAssignOperatorClass:
case Stmt::BinaryOperatorClass: {
const BinaryOperator *BinOp1 = cast<BinaryOperator>(Stmt1);
const BinaryOperator *BinOp2 = cast<BinaryOperator>(Stmt2);
return BinOp1->getOpcode() == BinOp2->getOpcode();
}
case Stmt::CharacterLiteralClass: {
const CharacterLiteral *CharLit1 = cast<CharacterLiteral>(Stmt1);
const CharacterLiteral *CharLit2 = cast<CharacterLiteral>(Stmt2);
return CharLit1->getValue() == CharLit2->getValue();
}
case Stmt::DeclRefExprClass: {
const DeclRefExpr *DeclRef1 = cast<DeclRefExpr>(Stmt1);
const DeclRefExpr *DeclRef2 = cast<DeclRefExpr>(Stmt2);
return DeclRef1->getDecl() == DeclRef2->getDecl();
}
case Stmt::IntegerLiteralClass: {
const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Stmt1);
const IntegerLiteral *IntLit2 = cast<IntegerLiteral>(Stmt2);
llvm::APInt I1 = IntLit1->getValue();
llvm::APInt I2 = IntLit2->getValue();
if (I1.getBitWidth() != I2.getBitWidth())
return false;
return I1 == I2;
}
case Stmt::FloatingLiteralClass: {
const FloatingLiteral *FloatLit1 = cast<FloatingLiteral>(Stmt1);
const FloatingLiteral *FloatLit2 = cast<FloatingLiteral>(Stmt2);
return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue());
}
case Stmt::StringLiteralClass: {
const StringLiteral *StringLit1 = cast<StringLiteral>(Stmt1);
const StringLiteral *StringLit2 = cast<StringLiteral>(Stmt2);
return StringLit1->getBytes() == StringLit2->getBytes();
}
case Stmt::MemberExprClass: {
const MemberExpr *MemberStmt1 = cast<MemberExpr>(Stmt1);
const MemberExpr *MemberStmt2 = cast<MemberExpr>(Stmt2);
return MemberStmt1->getMemberDecl() == MemberStmt2->getMemberDecl();
}
case Stmt::UnaryOperatorClass: {
const UnaryOperator *UnaryOp1 = cast<UnaryOperator>(Stmt1);
const UnaryOperator *UnaryOp2 = cast<UnaryOperator>(Stmt2);
return UnaryOp1->getOpcode() == UnaryOp2->getOpcode();
}
}
}
//===----------------------------------------------------------------------===//
// FindIdenticalExprChecker
//===----------------------------------------------------------------------===//
namespace {
class FindIdenticalExprChecker : public Checker<check::ASTCodeBody> {
public:
void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
BugReporter &BR) const {
FindIdenticalExprVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D));
Visitor.TraverseDecl(const_cast<Decl *>(D));
}
};
} // end anonymous namespace
void ento::registerIdenticalExprChecker(CheckerManager &Mgr) {
Mgr.registerChecker<FindIdenticalExprChecker>();
}
bool ento::shouldRegisterIdenticalExprChecker(const CheckerManager &mgr) {
return true;
}