[clang-tidy] Add check 'bugprone-assignment-in-selection-statement' (#180219)
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "AssignmentInSelectionStatementCheck.h"
|
||||
#include "clang/AST/IgnoreExpr.h"
|
||||
#include "clang/AST/StmtVisitor.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "llvm/ADT/TypeSwitch.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace {
|
||||
|
||||
class ConditionValueCanPropagateFrom
|
||||
: public ConstStmtVisitor<ConditionValueCanPropagateFrom, void> {
|
||||
public:
|
||||
llvm::SmallVector<const Expr *, 2> ExprToProcess;
|
||||
|
||||
void VisitBinaryOperator(const BinaryOperator *BO) {
|
||||
if (BO->isCommaOp())
|
||||
ExprToProcess.push_back(BO->getRHS()->IgnoreParenImpCasts());
|
||||
}
|
||||
void VisitAbstractConditionalOperator(const AbstractConditionalOperator *CO) {
|
||||
ExprToProcess.push_back(CO->getFalseExpr()->IgnoreParenImpCasts());
|
||||
ExprToProcess.push_back(CO->getTrueExpr()->IgnoreParenImpCasts());
|
||||
}
|
||||
};
|
||||
|
||||
AST_MATCHER_P(Expr, conditionValueCanPropagateFrom,
|
||||
ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
|
||||
bool Found = false;
|
||||
ConditionValueCanPropagateFrom Visitor;
|
||||
Visitor.Visit(&Node); // Do not match Node itself.
|
||||
while (!Visitor.ExprToProcess.empty()) {
|
||||
const Expr *E = Visitor.ExprToProcess.pop_back_val();
|
||||
ast_matchers::internal::BoundNodesTreeBuilder Result;
|
||||
if (InnerMatcher.matches(*E, Finder, &Result)) {
|
||||
Found = true;
|
||||
Builder->addMatch(Result);
|
||||
}
|
||||
Visitor.Visit(E);
|
||||
}
|
||||
return Found;
|
||||
}
|
||||
|
||||
// Ignore implicit casts (including C++ conversion member calls) but not parens.
|
||||
AST_MATCHER_P(Expr, ignoringImplicitAsWritten,
|
||||
ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
|
||||
auto IgnoreImplicitMemberCallSingleStep = [](Expr *E) {
|
||||
if (auto *C = dyn_cast<CXXMemberCallExpr>(E)) {
|
||||
Expr *ExprNode = C->getImplicitObjectArgument();
|
||||
if (ExprNode->getSourceRange() == E->getSourceRange())
|
||||
return ExprNode;
|
||||
ExprNode = ExprNode->IgnoreParenImpCasts();
|
||||
if (ExprNode->getSourceRange() == E->getSourceRange())
|
||||
return ExprNode;
|
||||
}
|
||||
return E;
|
||||
};
|
||||
|
||||
const Expr *IgnoreE = IgnoreExprNodes(&Node, IgnoreImplicitSingleStep,
|
||||
IgnoreImplicitCastsExtraSingleStep,
|
||||
IgnoreImplicitMemberCallSingleStep);
|
||||
|
||||
return InnerMatcher.matches(*IgnoreE, Finder, Builder);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace clang::tidy::bugprone {
|
||||
|
||||
void AssignmentInSelectionStatementCheck::registerMatchers(
|
||||
MatchFinder *Finder) {
|
||||
auto AssignOpNoParens = ignoringImplicitAsWritten(
|
||||
binaryOperation(hasOperatorName("=")).bind("assignment"));
|
||||
auto AssignOpMaybeParens = ignoringParenImpCasts(
|
||||
binaryOperation(hasOperatorName("=")).bind("assignment"));
|
||||
auto AssignOpFromEmbeddedExpr = expr(ignoringParenImpCasts(
|
||||
conditionValueCanPropagateFrom(AssignOpMaybeParens)));
|
||||
|
||||
auto CondExprWithAssign = anyOf(AssignOpNoParens, AssignOpFromEmbeddedExpr);
|
||||
auto OpCondExprWithAssign =
|
||||
anyOf(AssignOpMaybeParens, AssignOpFromEmbeddedExpr);
|
||||
|
||||
// In these cases "single primary expression" is possible.
|
||||
// A single assignment within a 'ParenExpr' is allowed (but not if mixed with
|
||||
// other operators).
|
||||
auto FoundControlStmt = mapAnyOf(ifStmt, whileStmt, doStmt, forStmt)
|
||||
.with(hasCondition(CondExprWithAssign));
|
||||
// In these cases "single primary expression" is not possible because the
|
||||
// assignment is already part of a bigger expression.
|
||||
auto FoundConditionalOperator =
|
||||
mapAnyOf(conditionalOperator, binaryConditionalOperator)
|
||||
.with(hasCondition(OpCondExprWithAssign));
|
||||
auto FoundLogicalOp = binaryOperator(
|
||||
hasAnyOperatorName("&&", "||"),
|
||||
eachOf(hasLHS(OpCondExprWithAssign), hasRHS(OpCondExprWithAssign)));
|
||||
|
||||
auto FoundSelectionStmt =
|
||||
stmt(anyOf(FoundControlStmt, FoundConditionalOperator, FoundLogicalOp))
|
||||
.bind("parent");
|
||||
|
||||
Finder->addMatcher(FoundSelectionStmt, this);
|
||||
}
|
||||
|
||||
void AssignmentInSelectionStatementCheck::check(
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
const auto *FoundAssignment = Result.Nodes.getNodeAs<Stmt>("assignment");
|
||||
assert(FoundAssignment);
|
||||
|
||||
const auto *ParentStmt = Result.Nodes.getNodeAs<Stmt>("parent");
|
||||
const StringRef CondStr =
|
||||
llvm::TypeSwitch<const Stmt *, const char *>(ParentStmt)
|
||||
.Case([](const IfStmt *) { return "condition of 'if' statement"; })
|
||||
.Case<WhileStmt, DoStmt, ForStmt>(
|
||||
[](const Stmt *) { return "condition of a loop"; })
|
||||
.Case([](const ConditionalOperator *) {
|
||||
return "condition of a ternary operator";
|
||||
})
|
||||
.Case([](const BinaryOperator *) {
|
||||
return "operand of a logical operator";
|
||||
})
|
||||
.DefaultUnreachable();
|
||||
|
||||
const SourceLocation OpLoc =
|
||||
llvm::TypeSwitch<const Stmt *, SourceLocation>(FoundAssignment)
|
||||
.Case<BinaryOperator, CXXOperatorCallExpr>(
|
||||
[](const auto *Op) { return Op->getOperatorLoc(); })
|
||||
.Default(FoundAssignment->getBeginLoc());
|
||||
diag(OpLoc, "assignment within %0 may indicate programmer error")
|
||||
<< FoundAssignment->getSourceRange() << CondStr;
|
||||
diag(OpLoc, "if it should be an assignment, move it out of the condition",
|
||||
DiagnosticIDs::Note);
|
||||
diag(OpLoc, "if it is meant to be an equality check, change '=' to '=='",
|
||||
DiagnosticIDs::Note);
|
||||
}
|
||||
|
||||
} // namespace clang::tidy::bugprone
|
||||
@@ -0,0 +1,30 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_ASSIGNMENTINSELECTIONSTATEMENTCHECK_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_ASSIGNMENTINSELECTIONSTATEMENTCHECK_H
|
||||
|
||||
#include "../ClangTidyCheck.h"
|
||||
|
||||
namespace clang::tidy::bugprone {
|
||||
|
||||
/// Finds assignments within selection statements.
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// https://clang.llvm.org/extra/clang-tidy/checks/bugprone/assignment-in-selection-statement.html
|
||||
class AssignmentInSelectionStatementCheck : public ClangTidyCheck {
|
||||
public:
|
||||
AssignmentInSelectionStatementCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
|
||||
} // namespace clang::tidy::bugprone
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_ASSIGNMENTINSELECTIONSTATEMENTCHECK_H
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "ArgumentCommentCheck.h"
|
||||
#include "AssertSideEffectCheck.h"
|
||||
#include "AssignmentInIfConditionCheck.h"
|
||||
#include "AssignmentInSelectionStatementCheck.h"
|
||||
#include "BadSignalToKillThreadCheck.h"
|
||||
#include "BitwisePointerCastCheck.h"
|
||||
#include "BoolPointerImplicitConversionCheck.h"
|
||||
@@ -127,6 +128,8 @@ public:
|
||||
"bugprone-assert-side-effect");
|
||||
CheckFactories.registerCheck<AssignmentInIfConditionCheck>(
|
||||
"bugprone-assignment-in-if-condition");
|
||||
CheckFactories.registerCheck<AssignmentInSelectionStatementCheck>(
|
||||
"bugprone-assignment-in-selection-statement");
|
||||
CheckFactories.registerCheck<BadSignalToKillThreadCheck>(
|
||||
"bugprone-bad-signal-to-kill-thread");
|
||||
CheckFactories.registerCheck<BitwisePointerCastCheck>(
|
||||
|
||||
@@ -7,6 +7,7 @@ add_clang_library(clangTidyBugproneModule STATIC
|
||||
ArgumentCommentCheck.cpp
|
||||
AssertSideEffectCheck.cpp
|
||||
AssignmentInIfConditionCheck.cpp
|
||||
AssignmentInSelectionStatementCheck.cpp
|
||||
BadSignalToKillThreadCheck.cpp
|
||||
BitwisePointerCastCheck.cpp
|
||||
BoolPointerImplicitConversionCheck.cpp
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "../ClangTidy.h"
|
||||
#include "../ClangTidyModule.h"
|
||||
#include "../bugprone/AssignmentInSelectionStatementCheck.h"
|
||||
#include "../bugprone/BadSignalToKillThreadCheck.h"
|
||||
#include "../bugprone/CommandProcessorCheck.h"
|
||||
#include "../bugprone/CopyConstructorMutatesArgumentCheck.h"
|
||||
@@ -308,6 +309,8 @@ public:
|
||||
// EXP
|
||||
CheckFactories.registerCheck<bugprone::SuspiciousMemoryComparisonCheck>(
|
||||
"cert-exp42-c");
|
||||
CheckFactories.registerCheck<bugprone::AssignmentInSelectionStatementCheck>(
|
||||
"cert-exp45-c");
|
||||
// FLP
|
||||
CheckFactories.registerCheck<bugprone::FloatLoopCounterCheck>(
|
||||
"cert-flp30-c");
|
||||
|
||||
@@ -114,6 +114,11 @@ Improvements to clang-tidy
|
||||
New checks
|
||||
^^^^^^^^^^
|
||||
|
||||
- New :doc:`bugprone-assignment-in-selection-statement
|
||||
<clang-tidy/checks/bugprone/assignment-in-selection-statement>` check.
|
||||
|
||||
Finds assignments within selection statements.
|
||||
|
||||
- New :doc:`bugprone-unsafe-to-allow-exceptions
|
||||
<clang-tidy/checks/bugprone/unsafe-to-allow-exceptions>` check.
|
||||
|
||||
@@ -178,6 +183,10 @@ New checks
|
||||
New check aliases
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
- New alias :doc:`cert-exp45-c <clang-tidy/checks/cert/exp45-c>`
|
||||
to :doc:`bugprone-assignment-in-selection-statement
|
||||
<clang-tidy/checks/bugprone/assignment-in-selection-statement>`.
|
||||
|
||||
- Renamed :doc:`hicpp-exception-baseclass
|
||||
<clang-tidy/checks/hicpp/exception-baseclass>`
|
||||
to :doc:`bugprone-std-exception-baseclass
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
.. title:: clang-tidy - bugprone-assignment-in-selection-statement
|
||||
|
||||
bugprone-assignment-in-selection-statement
|
||||
==========================================
|
||||
|
||||
Finds assignments within selection statements.
|
||||
Such assignments may indicate programmer error because they may have been
|
||||
intended as equality tests. The selection statements are conditions of ``if``
|
||||
and loop (``for``, ``while``, ``do``) statements, condition of conditional
|
||||
operator (``?:``) and any operand of a binary logical operator (``&&``,
|
||||
``||``). The check finds assignments within these contexts if the single
|
||||
expression is an assignment or the assignment is contained (recursively) in
|
||||
last operand of a comma (``,``) operator or true and false expressions in a
|
||||
conditional operator. The warning is suppressed if the assignment is placed in
|
||||
extra parentheses, but only if the assignment is the single expression of a
|
||||
condition (of ``if`` or a loop statement).
|
||||
|
||||
This check corresponds to the CERT rule
|
||||
`EXP45-C. Do not perform assignments in selection statements
|
||||
<https://wiki.sei.cmu.edu/confluence/spaces/c/pages/87152228/EXP45-C.+Do+not+perform+assignments+in+selection+statements>`_.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
The check emits a warning in the following cases at the indicated locations:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
int x = 3;
|
||||
|
||||
if (x = 4) // should it be `x == 4` instead of 'x = 4' ?
|
||||
x = x + 1;
|
||||
|
||||
while ((x <= 11) || (x = 22)) // assignment appears as operand of a logical operator
|
||||
x += 2;
|
||||
|
||||
do {
|
||||
x += 5;
|
||||
} while ((x > 10) ? (x = 11) : (x > 5)); // assignment in loop condition (from `x = 11`)
|
||||
|
||||
for (int i = 0; i == 2, x = 5; ++i) // assignment in loop condition (from last operand of comma)
|
||||
foo1(i, x);
|
||||
|
||||
for (int i = 0; i == 2, (x = 5); ++i) // assignment is not a single expression, parentheses do not prevent the warning
|
||||
foo1(i, x);
|
||||
|
||||
int a = (x == 2) || (x = 3); // assignment appears in the operand a logical operator
|
||||
|
||||
The following cases do not produce a warning:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
if ((x = 1)) { // a single assignment between parentheses
|
||||
x += 10;
|
||||
|
||||
if ((x = 1) != 0) { // assignment appears in a complex expression and without a logical operator
|
||||
++x;
|
||||
|
||||
if (foo(x = 9) && array[x = 8]) { // assignment appears in argument of function call or array index
|
||||
++x;
|
||||
|
||||
for (int i = 0; i = 2, x == 5; ++i) // assignment does not take part in the condition of the loop
|
||||
foo1(i, x);
|
||||
@@ -0,0 +1,9 @@
|
||||
.. meta::
|
||||
:http-equiv=refresh: 5;URL=../bugprone/assignment-in-selection-statement.html
|
||||
|
||||
cert-exp45-c
|
||||
============
|
||||
|
||||
The `cert-exp45-c` check is an alias, please see
|
||||
:doc:`bugprone-assignment-in-selection-statement
|
||||
<../bugprone/assignment-in-selection-statement>` for more information.
|
||||
@@ -81,6 +81,7 @@ Clang-Tidy Checks
|
||||
:doc:`bugprone-argument-comment <bugprone/argument-comment>`, "Yes"
|
||||
:doc:`bugprone-assert-side-effect <bugprone/assert-side-effect>`,
|
||||
:doc:`bugprone-assignment-in-if-condition <bugprone/assignment-in-if-condition>`,
|
||||
:doc:`bugprone-assignment-in-selection-statement <bugprone/assignment-in-selection-statement>`,
|
||||
:doc:`bugprone-bad-signal-to-kill-thread <bugprone/bad-signal-to-kill-thread>`,
|
||||
:doc:`bugprone-bitwise-pointer-cast <bugprone/bitwise-pointer-cast>`,
|
||||
:doc:`bugprone-bool-pointer-implicit-conversion <bugprone/bool-pointer-implicit-conversion>`, "Yes"
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
// RUN: %check_clang_tidy %s bugprone-assignment-in-selection-statement %t
|
||||
|
||||
void test_if(int a, int b, int c, int d) {
|
||||
if (a = b) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: assignment within condition of 'if' statement may indicate programmer error
|
||||
if ((a = b)) {}
|
||||
if (a == b) {}
|
||||
|
||||
if ((b > 0) ? (a = b) : c) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: assignment within condition of 'if' statement may indicate programmer error
|
||||
if ((b > 0) ? c : (a = b)) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: assignment within condition of 'if' statement may indicate programmer error
|
||||
if (a = c, b = c) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: assignment within condition of 'if' statement may indicate programmer error
|
||||
if ((b > 0) ?: (a = b)) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: assignment within condition of 'if' statement may indicate programmer error
|
||||
if ((b = c) ?: (a > b)) {}
|
||||
// FIXME: why is this not found?
|
||||
}
|
||||
|
||||
void test_while(int a, int b, int c) {
|
||||
while (a = b) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: assignment within condition of a loop may indicate programmer error
|
||||
while ((a = b)) {}
|
||||
while (a == b) {}
|
||||
|
||||
while ((b > 0) ? (a = b) : c) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: assignment within condition of a loop may indicate programmer error
|
||||
while ((b > 0) ? c : (a = b)) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: assignment within condition of a loop may indicate programmer error
|
||||
while (a = b, b = c) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: assignment within condition of a loop may indicate programmer error
|
||||
}
|
||||
|
||||
void test_do(int a, int b, int c) {
|
||||
do {} while (a = b);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: assignment within condition of a loop may indicate programmer error
|
||||
do {} while ((a = b));
|
||||
do {} while (a == b);
|
||||
|
||||
do {} while ((b > 0) ? (a = b) : c);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: assignment within condition of a loop may indicate programmer error
|
||||
do {} while ((b > 0) ? c : (a = b));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: assignment within condition of a loop may indicate programmer error
|
||||
}
|
||||
|
||||
void test_for(int a, int b, int c) {
|
||||
for (int i = 0; a = b; i++) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: assignment within condition of a loop may indicate programmer error
|
||||
for (int i = 0; (a = b); i++) {}
|
||||
for (int i = 0; a == b; i++) {}
|
||||
|
||||
for (int i = 0; (b > 0) ? (a = b) : c; ++i) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: assignment within condition of a loop may indicate programmer error
|
||||
for (int i = 0; (b > 0) ? c : (a = b); ++i) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: assignment within condition of a loop may indicate programmer error
|
||||
}
|
||||
|
||||
void test_conditional(int a, int b, int c, int d) {
|
||||
int c1 = (a = b) ? 1 : 2;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: assignment within condition of a ternary operator may indicate programmer error
|
||||
int c2 = ((a = b)) ? 1 : 2;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: assignment within condition of a ternary operator may indicate programmer error
|
||||
int c3 = (a == b) ? 1 : 2;
|
||||
|
||||
if ((c ? (a = b) : d) ? 1 : -1) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: assignment within condition of a ternary operator may indicate programmer error
|
||||
if ((c ? ((a = b)) : d) ? 1 : -1) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: assignment within condition of a ternary operator may indicate programmer error
|
||||
while ((c ? d : (a = b)) ? 1 : -1) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: assignment within condition of a ternary operator may indicate programmer error
|
||||
|
||||
int c4 = (c ? (a = b) : 2);
|
||||
int c5 = (c ? 2 : (a = b));
|
||||
}
|
||||
|
||||
void test_bin_op(int a, int b, int c, int d) {
|
||||
int c1 = (a = b) && c;
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: assignment within operand of a logical operator may indicate programmer error
|
||||
int c2 = c || (a = b);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: assignment within operand of a logical operator may indicate programmer error
|
||||
int c3 = ((a = b) && c) || (c == b - a);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: assignment within operand of a logical operator may indicate programmer error
|
||||
int c4 = c || ((a = b));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: assignment within operand of a logical operator may indicate programmer error
|
||||
int c5 = (a = b) + 2;
|
||||
int c6 = ((a = b) + 2) && c;
|
||||
}
|
||||
|
||||
int f(int);
|
||||
|
||||
void test_misc(int a, int b, int c, int d) {
|
||||
if ((a = c, b = c)) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: assignment within condition of 'if' statement may indicate programmer error
|
||||
if (a = c, (b = c)) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: assignment within condition of 'if' statement may indicate programmer error
|
||||
if ((a > 0) ? ((b < 0) ? (a = b) : (a = c)) : (a = d)) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: assignment within condition of 'if' statement may indicate programmer error
|
||||
// CHECK-MESSAGES: :[[@LINE-2]]:41: warning: assignment within condition of 'if' statement may indicate programmer error
|
||||
// CHECK-MESSAGES: :[[@LINE-3]]:52: warning: assignment within condition of 'if' statement may indicate programmer error
|
||||
while (a = c, (b = c, c = d)) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: assignment within condition of a loop may indicate programmer error
|
||||
for (d = 0; a = c, b = c, ((a > 0) ? d == a : (d = b)); ++d) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:52: warning: assignment within condition of a loop may indicate programmer error
|
||||
do {} while ((a > 0) ? (a = c, b = c) : d);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: assignment within condition of a loop may indicate programmer error
|
||||
if ((a = b) && (c = d)) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: assignment within operand of a logical operator may indicate programmer error
|
||||
// CHECK-MESSAGES: :[[@LINE-2]]:21: warning: assignment within operand of a logical operator may indicate programmer error
|
||||
if ((a ? (b = c) : d) && (d ? c : (b = a))) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: assignment within operand of a logical operator may indicate programmer error
|
||||
// CHECK-MESSAGES: :[[@LINE-2]]:40: warning: assignment within operand of a logical operator may indicate programmer error
|
||||
if (f((a = b) ? c : d)) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: assignment within condition of a ternary operator may indicate programmer error
|
||||
}
|
||||
|
||||
void test_no_warning(int a, int b, int c) {
|
||||
if ((a = b) != 0) {}
|
||||
if (!(a = b)) {}
|
||||
if ((int)(a = b)) {}
|
||||
if ((a = b) + c > 0) {}
|
||||
if ((b > 0) ? (a == b) : c) {}
|
||||
if ((b > 0) ? c : (a == b)) {}
|
||||
if (a = c, b == c) {}
|
||||
|
||||
int arr[10] = {0};
|
||||
if (f(a = b)) {}
|
||||
if (arr[c = a]) {};
|
||||
|
||||
switch (a = b) {}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// RUN: %check_clang_tidy %s -std=c++17-or-later bugprone-assignment-in-selection-statement %t
|
||||
|
||||
struct S {
|
||||
int A = 1;
|
||||
S &operator=(const S &s) { A = s.A; return *this; }
|
||||
operator bool() { return A == 1; }
|
||||
};
|
||||
|
||||
void test(S a) {
|
||||
S x;
|
||||
int y, z;
|
||||
|
||||
if (x = a) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: assignment within condition of 'if' statement may indicate programmer error
|
||||
if (int x = y; x = 0) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: assignment within condition of 'if' statement may indicate programmer error
|
||||
|
||||
if ((x = a)) {}
|
||||
if ((x = a).A > 1) {}
|
||||
if (static_cast<bool>(x = a)) {}
|
||||
if (int x = y; x > 0) {}
|
||||
if ([&y](int i) { return y = i; }(z = 2)) {}
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void test_fold(int x, Args... args) {
|
||||
if ((... || (args = x))) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: assignment within operand of a logical operator may indicate programmer error
|
||||
if ((... = args) && x > 2) {}
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: assignment within operand of a logical operator may indicate programmer error
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void test_fold_arg(Args... args) {
|
||||
if ((... && args)) {}
|
||||
}
|
||||
|
||||
void test1(int x1, int x2, int y1, int y2) {
|
||||
test_fold(1, 2, x1);
|
||||
test_fold_arg(x1 == y1, x2 = y2);
|
||||
}
|
||||
Reference in New Issue
Block a user