[clang-tools-extra] [clang-tidy] Add check 'bugprone-assignment-in-selection-statement' (PR #180219)
Balázs Kéri via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 13 07:03:12 PST 2026
https://github.com/balazske updated https://github.com/llvm/llvm-project/pull/180219
>From c5a709e3935e69cd16de70e198e6b469a6e3826e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <balazs.keri at ericsson.com>
Date: Mon, 2 Feb 2026 12:55:30 +0100
Subject: [PATCH 1/3] [clang-tidy] Add check
'bugprone-assignment-in-selection-statement'
---
.../AssignmentInSelectionStatementCheck.cpp | 119 +++++++++++++++++
.../AssignmentInSelectionStatementCheck.h | 30 +++++
.../bugprone/BugproneTidyModule.cpp | 3 +
.../clang-tidy/bugprone/CMakeLists.txt | 1 +
.../clang-tidy/cert/CERTTidyModule.cpp | 3 +
clang-tools-extra/docs/ReleaseNotes.rst | 5 +
.../assignment-in-selection-statement.rst | 55 ++++++++
.../docs/clang-tidy/checks/list.rst | 1 +
.../assignment-in-selection-statement.c | 124 ++++++++++++++++++
9 files changed, 341 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/bugprone/AssignmentInSelectionStatementCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/bugprone/AssignmentInSelectionStatementCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/assignment-in-selection-statement.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/assignment-in-selection-statement.c
diff --git a/clang-tools-extra/clang-tidy/bugprone/AssignmentInSelectionStatementCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/AssignmentInSelectionStatementCheck.cpp
new file mode 100644
index 0000000000000..981a48a734a50
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/AssignmentInSelectionStatementCheck.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/StmtVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.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 VisitConditionalOperator(const ConditionalOperator *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;
+}
+
+} // namespace
+
+namespace clang::tidy::bugprone {
+
+void AssignmentInSelectionStatementCheck::registerMatchers(
+ MatchFinder *Finder) {
+ auto AssignOp = binaryOperation(hasOperatorName("=")).bind("assignment");
+
+ auto CondExprWithAssign = expr(
+ anyOf(ignoringImpCasts(AssignOp),
+ ignoringParenImpCasts(conditionValueCanPropagateFrom(AssignOp))));
+ auto OpCondExprWithAssign = expr(ignoringParenImpCasts(
+ anyOf(AssignOp, conditionValueCanPropagateFrom(AssignOp))));
+
+ // 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 =
+ conditionalOperator(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<BinaryOperator>("assignment");
+ if (!FoundAssignment)
+ return;
+ const auto *ParentStmt = Result.Nodes.getNodeAs<Stmt>("parent");
+ const char *CondStr = nullptr;
+ switch (ParentStmt->getStmtClass()) {
+ case Stmt::IfStmtClass:
+ CondStr = "condition of 'if' statement";
+ break;
+ case Stmt::WhileStmtClass:
+ CondStr = "condition of 'while' statement";
+ break;
+ case Stmt::DoStmtClass:
+ CondStr = "condition of 'do' statement";
+ break;
+ case Stmt::ForStmtClass:
+ CondStr = "condition of 'for' statement";
+ break;
+ case Stmt::ConditionalOperatorClass:
+ CondStr = "condition of conditional operator";
+ break;
+ case Stmt::BinaryOperatorClass:
+ CondStr = "operand of a logical operator";
+ break;
+ default:
+ llvm_unreachable("unexpected statement class, should not match");
+ };
+ diag(FoundAssignment->getOperatorLoc(),
+ "Assignment within %0 may indicate programmer error")
+ << FoundAssignment->getSourceRange() << CondStr;
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/clang-tidy/bugprone/AssignmentInSelectionStatementCheck.h b/clang-tools-extra/clang-tidy/bugprone/AssignmentInSelectionStatementCheck.h
new file mode 100644
index 0000000000000..4de1f87b3b3ce
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/AssignmentInSelectionStatementCheck.h
@@ -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
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 4150442c25d61..c02c1c4d8b657 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -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"
@@ -125,6 +126,8 @@ class BugproneModule : public ClangTidyModule {
"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>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index db1256d91d311..4ce1bab8881c5 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -7,6 +7,7 @@ add_clang_library(clangTidyBugproneModule STATIC
ArgumentCommentCheck.cpp
AssertSideEffectCheck.cpp
AssignmentInIfConditionCheck.cpp
+ AssignmentInSelectionStatementCheck.cpp
BadSignalToKillThreadCheck.cpp
BitwisePointerCastCheck.cpp
BoolPointerImplicitConversionCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
index f64cb47d18b4e..d18a8253b66a9 100644
--- a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.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"
@@ -311,6 +312,8 @@ class CERTModule : public ClangTidyModule {
// EXP
CheckFactories.registerCheck<bugprone::SuspiciousMemoryComparisonCheck>(
"cert-exp42-c");
+ CheckFactories.registerCheck<bugprone::AssignmentInSelectionStatementCheck>(
+ "cert-exp45-c");
// FLP
CheckFactories.registerCheck<bugprone::FloatLoopCounterCheck>(
"cert-flp30-c");
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 1a056890e66c3..4c71f81ca5f59 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -97,6 +97,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:`llvm-use-vector-utils
<clang-tidy/checks/llvm/use-vector-utils>` check.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/assignment-in-selection-statement.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/assignment-in-selection-statement.rst
new file mode 100644
index 0000000000000..a56ce75f8a2bf
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/assignment-in-selection-statement.rst
@@ -0,0 +1,55 @@
+.. 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.
+There is no warning if a single-standing assignment is enclosed in parentheses.
+
+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
+========
+
+.. code-block:: c++
+
+ int x = 3;
+
+ if (x = 4) // warning: should it be `x == 4`?
+ x = x + 1;
+
+ if ((x = 1)) { // no warning: single assignment in parentheses
+ x += 10;
+
+ if ((x = 1) != 0) { // no warning: assignment appears in a complex expression and not with a logical operator
+ ++x;
+
+ if (foo(x = 9) && array[x = 8]) { // no warning: assignment appears in argument of function call or array index
+ ++x;
+
+ while ((x <= 11) || (x = 22)) // warning: the assignment is found as operand of a logical operator
+ x += 2;
+
+ do {
+ x += 5;
+ } while ((x > 10) ? (x = 11) : (x > 5)); // warning: assignment in loop condition (from `x = 11`)
+
+ for (int i = 0; i == 2, x = 5; ++i) // warning: assignment in loop condition (from last operand of comma)
+ foo1(i, x);
+
+ for (int i = 0; i == 2, (x = 5); ++i) // warning: assignment is not a single expression, parentheses do not prevent the warning
+ foo1(i, x);
+
+ for (int i = 0; i = 2, x == 5; ++i) // no warning: assignment does not take part in the condition of the loop
+ foo1(i, x);
+
+ int a = (x == 2) || (x = 3); // warning: the assignment appears in the operand a logical operator
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 25d1354fc4c20..8f3b04a9980cf 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -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"
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/assignment-in-selection-statement.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/assignment-in-selection-statement.c
new file mode 100644
index 0000000000000..8b71275edab35
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/assignment-in-selection-statement.c
@@ -0,0 +1,124 @@
+// 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 programming 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 programming error
+ if ((b > 0) ? c : (a = b)) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: Assignment within condition of 'if' statement may indicate programming error
+ if (a = c, b = c) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: Assignment within condition of 'if' statement may indicate programming error
+}
+
+void test_while(int a, int b, int c) {
+ while (a = b) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Assignment within condition of 'while' statement may indicate programming error
+ while ((a = b)) {}
+ while (a == b) {}
+
+ while ((b > 0) ? (a = b) : c) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: Assignment within condition of 'while' statement may indicate programming error
+ while ((b > 0) ? c : (a = b)) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: Assignment within condition of 'while' statement may indicate programming error
+ while (a = b, b = c) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: Assignment within condition of 'while' statement may indicate programming error
+}
+
+void test_do(int a, int b, int c) {
+ do {} while (a = b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: Assignment within condition of 'do' statement may indicate programming 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 'do' statement may indicate programming error
+ do {} while ((b > 0) ? c : (a = b));
+ // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: Assignment within condition of 'do' statement may indicate programming 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 'for' statement may indicate programming 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 'for' statement may indicate programming error
+ for (int i = 0; (b > 0) ? c : (a = b); ++i) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: Assignment within condition of 'for' statement may indicate programming 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 conditional operator may indicate programming error
+ int c2 = ((a = b)) ? 1 : 2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: Assignment within condition of conditional operator may indicate programming error
+ int c3 = (a == b) ? 1 : 2;
+
+ if ((c ? (a = b) : d) ? 1 : -1) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: Assignment within condition of conditional operator may indicate programming error
+ while ((c ? d : (a = b)) ? 1 : -1) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: Assignment within condition of conditional operator may indicate programming 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 programming error
+ int c2 = c || (a = b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: Assignment within operand of a logical operator may indicate programming error
+ int c3 = ((a = b) && c) || (c == b - a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: Assignment within operand of a logical operator may indicate programming error
+ int c4 = c || ((a = b));
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: Assignment within operand of a logical operator may indicate programming 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 programming error
+ if (a = c, (b = c)) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: Assignment within condition of 'if' statement may indicate programming 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 programming error
+ // CHECK-MESSAGES: :[[@LINE-2]]:41: warning: Assignment within condition of 'if' statement may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-3]]:52: warning: Assignment within condition of 'if' statement may indicate programming error
+ while (a = c, (b = c, c = d)) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: Assignment within condition of 'while' statement may indicate programming 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 'for' statement may indicate programming error
+ do {} while ((a > 0) ? (a = c, b = c) : d);
+ // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: Assignment within condition of 'do' statement may indicate programming error
+ if ((a = b) && (c = d)) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: Assignment within operand of a logical operator may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-2]]:21: warning: Assignment within operand of a logical operator may indicate programming 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 programming error
+ // CHECK-MESSAGES: :[[@LINE-2]]:40: warning: Assignment within operand of a logical operator may indicate programming error
+ if (f((a = b) ? c : d)) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Assignment within condition of conditional operator may indicate programming 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]) {};
+}
>From 33c9be7a5822d0d9ac45629dde02714ff8b1753c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <balazs.keri at ericsson.com>
Date: Fri, 6 Feb 2026 16:53:29 +0100
Subject: [PATCH 2/3] fixed test
---
.../assignment-in-selection-statement.c | 70 +++++++++----------
1 file changed, 35 insertions(+), 35 deletions(-)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/assignment-in-selection-statement.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/assignment-in-selection-statement.c
index 8b71275edab35..594dcc0feac4f 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/assignment-in-selection-statement.c
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/assignment-in-selection-statement.c
@@ -2,67 +2,67 @@
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 programming error
+ // 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 programming error
+ // 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 programming error
+ // 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 programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: Assignment within condition of 'if' statement may indicate programmer error
}
void test_while(int a, int b, int c) {
while (a = b) {}
- // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Assignment within condition of 'while' statement may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Assignment within condition of 'while' statement 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 'while' statement may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: Assignment within condition of 'while' statement may indicate programmer error
while ((b > 0) ? c : (a = b)) {}
- // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: Assignment within condition of 'while' statement may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: Assignment within condition of 'while' statement may indicate programmer error
while (a = b, b = c) {}
- // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: Assignment within condition of 'while' statement may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: Assignment within condition of 'while' statement 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 'do' statement may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: Assignment within condition of 'do' statement 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 'do' statement may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: Assignment within condition of 'do' statement may indicate programmer error
do {} while ((b > 0) ? c : (a = b));
- // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: Assignment within condition of 'do' statement may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: Assignment within condition of 'do' statement 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 'for' statement may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: Assignment within condition of 'for' statement 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 'for' statement may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: Assignment within condition of 'for' statement may indicate programmer error
for (int i = 0; (b > 0) ? c : (a = b); ++i) {}
- // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: Assignment within condition of 'for' statement may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: Assignment within condition of 'for' statement 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 conditional operator may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: Assignment within condition of conditional operator may indicate programmer error
int c2 = ((a = b)) ? 1 : 2;
- // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: Assignment within condition of conditional operator may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: Assignment within condition of conditional 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 conditional operator may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: Assignment within condition of conditional operator may indicate programmer error
while ((c ? d : (a = b)) ? 1 : -1) {}
- // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: Assignment within condition of conditional operator may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: Assignment within condition of conditional operator may indicate programmer error
int c4 = (c ? (a = b) : 2);
int c5 = (c ? 2 : (a = b));
@@ -70,13 +70,13 @@ void test_conditional(int a, int b, int c, int d) {
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 programming error
+ // 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 programming error
+ // 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 programming error
+ // 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 programming error
+ // 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;
@@ -86,27 +86,27 @@ 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 programming error
+ // 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 programming error
+ // 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 programming error
- // CHECK-MESSAGES: :[[@LINE-2]]:41: warning: Assignment within condition of 'if' statement may indicate programming error
- // CHECK-MESSAGES: :[[@LINE-3]]:52: warning: Assignment within condition of 'if' statement may indicate programming error
+ // 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 'while' statement may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: Assignment within condition of 'while' statement 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 'for' statement may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:52: warning: Assignment within condition of 'for' statement may indicate programmer error
do {} while ((a > 0) ? (a = c, b = c) : d);
- // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: Assignment within condition of 'do' statement may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: Assignment within condition of 'do' statement may indicate programmer error
if ((a = b) && (c = d)) {}
- // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: Assignment within operand of a logical operator may indicate programming error
- // CHECK-MESSAGES: :[[@LINE-2]]:21: warning: Assignment within operand of a logical operator may indicate programming error
+ // 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 programming error
- // CHECK-MESSAGES: :[[@LINE-2]]:40: warning: Assignment within operand of a logical operator may indicate programming error
+ // 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 conditional operator may indicate programming error
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Assignment within condition of conditional operator may indicate programmer error
}
void test_no_warning(int a, int b, int c) {
>From 46ccb41fa44e034762aa37c58be762f91adc2f51 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <balazs.keri at ericsson.com>
Date: Fri, 13 Feb 2026 16:02:11 +0100
Subject: [PATCH 3/3] addressing most of the review comments
---
.../AssignmentInSelectionStatementCheck.cpp | 99 ++++++++++++-------
.../assignment-in-selection-statement.rst | 26 ++---
.../docs/clang-tidy/checks/cert/exp45-c.rst | 9 ++
.../assignment-in-selection-statement.c | 75 +++++++-------
.../assignment-in-selection-statement.cpp | 41 ++++++++
5 files changed, 167 insertions(+), 83 deletions(-)
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/cert/exp45-c.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/assignment-in-selection-statement.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/AssignmentInSelectionStatementCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/AssignmentInSelectionStatementCheck.cpp
index 981a48a734a50..f8f605733d5ea 100644
--- a/clang-tools-extra/clang-tidy/bugprone/AssignmentInSelectionStatementCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/AssignmentInSelectionStatementCheck.cpp
@@ -7,8 +7,10 @@
//===----------------------------------------------------------------------===//
#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;
@@ -47,19 +49,48 @@ AST_MATCHER_P(Expr, conditionValueCanPropagateFrom,
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;
+ if (auto *PE = dyn_cast<ParenExpr>(ExprNode)) {
+ if (PE->getSourceRange() == C->getSourceRange())
+ return cast<Expr>(PE);
+ }
+ 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 AssignOp = binaryOperation(hasOperatorName("=")).bind("assignment");
+ auto AssignOpNoParens = ignoringImplicitAsWritten(
+ binaryOperation(hasOperatorName("=")).bind("assignment"));
+ auto AssignOpMaybeParens = ignoringParenImpCasts(
+ binaryOperation(hasOperatorName("=")).bind("assignment"));
+ auto AssignOpFromEmbeddedExpr = expr(ignoringParenImpCasts(
+ conditionValueCanPropagateFrom(AssignOpMaybeParens)));
- auto CondExprWithAssign = expr(
- anyOf(ignoringImpCasts(AssignOp),
- ignoringParenImpCasts(conditionValueCanPropagateFrom(AssignOp))));
- auto OpCondExprWithAssign = expr(ignoringParenImpCasts(
- anyOf(AssignOp, conditionValueCanPropagateFrom(AssignOp))));
+ 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
@@ -83,37 +114,35 @@ void AssignmentInSelectionStatementCheck::registerMatchers(
void AssignmentInSelectionStatementCheck::check(
const MatchFinder::MatchResult &Result) {
- const auto *FoundAssignment =
- Result.Nodes.getNodeAs<BinaryOperator>("assignment");
- if (!FoundAssignment)
- return;
+ const auto *FoundAssignment = Result.Nodes.getNodeAs<Stmt>("assignment");
+ assert(FoundAssignment);
+
const auto *ParentStmt = Result.Nodes.getNodeAs<Stmt>("parent");
- const char *CondStr = nullptr;
- switch (ParentStmt->getStmtClass()) {
- case Stmt::IfStmtClass:
- CondStr = "condition of 'if' statement";
- break;
- case Stmt::WhileStmtClass:
- CondStr = "condition of 'while' statement";
- break;
- case Stmt::DoStmtClass:
- CondStr = "condition of 'do' statement";
- break;
- case Stmt::ForStmtClass:
- CondStr = "condition of 'for' statement";
- break;
- case Stmt::ConditionalOperatorClass:
- CondStr = "condition of conditional operator";
- break;
- case Stmt::BinaryOperatorClass:
- CondStr = "operand of a logical operator";
- break;
- default:
- llvm_unreachable("unexpected statement class, should not match");
- };
- diag(FoundAssignment->getOperatorLoc(),
- "Assignment within %0 may indicate programmer error")
+ StringRef CondStr =
+ llvm::TypeSwitch<const Stmt *, const char *>(ParentStmt)
+ .Case<IfStmt>(
+ [](const IfStmt *) { return "condition of 'if' statement"; })
+ .Case<WhileStmt, DoStmt, ForStmt>(
+ [](const Stmt *) { return "condition of a loop"; })
+ .Case<ConditionalOperator>([](const ConditionalOperator *) {
+ return "condition of a ternary operator";
+ })
+ .Case<BinaryOperator>([](const BinaryOperator *) {
+ return "operand of a logical operator";
+ })
+ .DefaultUnreachable();
+
+ 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
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/assignment-in-selection-statement.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/assignment-in-selection-statement.rst
index a56ce75f8a2bf..6ee5492c867ce 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/assignment-in-selection-statement.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/assignment-in-selection-statement.rst
@@ -11,7 +11,9 @@ 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.
-There is no warning if a single-standing assignment is enclosed in parentheses.
+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
@@ -27,15 +29,6 @@ Examples
if (x = 4) // warning: should it be `x == 4`?
x = x + 1;
- if ((x = 1)) { // no warning: single assignment in parentheses
- x += 10;
-
- if ((x = 1) != 0) { // no warning: assignment appears in a complex expression and not with a logical operator
- ++x;
-
- if (foo(x = 9) && array[x = 8]) { // no warning: assignment appears in argument of function call or array index
- ++x;
-
while ((x <= 11) || (x = 22)) // warning: the assignment is found as operand of a logical operator
x += 2;
@@ -49,7 +42,16 @@ Examples
for (int i = 0; i == 2, (x = 5); ++i) // warning: assignment is not a single expression, parentheses do not prevent the warning
foo1(i, x);
+ int a = (x == 2) || (x = 3); // warning: the assignment appears in the operand a logical operator
+
+ if ((x = 1)) { // no warning: single assignment in parentheses
+ x += 10;
+
+ if ((x = 1) != 0) { // no warning: assignment appears in a complex expression and not with a logical operator
+ ++x;
+
+ if (foo(x = 9) && array[x = 8]) { // no warning: assignment appears in argument of function call or array index
+ ++x;
+
for (int i = 0; i = 2, x == 5; ++i) // no warning: assignment does not take part in the condition of the loop
foo1(i, x);
-
- int a = (x == 2) || (x = 3); // warning: the assignment appears in the operand a logical operator
diff --git a/clang-tools-extra/docs/clang-tidy/checks/cert/exp45-c.rst b/clang-tools-extra/docs/clang-tidy/checks/cert/exp45-c.rst
new file mode 100644
index 0000000000000..e40a7c4403ec2
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/cert/exp45-c.rst
@@ -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.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/assignment-in-selection-statement.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/assignment-in-selection-statement.c
index 594dcc0feac4f..c64b886720d15 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/assignment-in-selection-statement.c
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/assignment-in-selection-statement.c
@@ -2,67 +2,69 @@
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
+ // 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
+ // 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
+ // 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
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: assignment within condition of 'if' statement may indicate programmer error
}
void test_while(int a, int b, int c) {
while (a = b) {}
- // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Assignment within condition of 'while' statement may indicate programmer error
+ // 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 'while' statement may indicate programmer error
+ // 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 'while' statement may indicate programmer error
+ // 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 'while' statement may indicate programmer error
+ // 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 'do' statement may indicate programmer error
+ // 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 'do' statement may indicate programmer error
+ // 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 'do' statement may indicate programmer error
+ // 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 'for' statement may indicate programmer error
+ // 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 'for' statement may indicate programmer error
+ // 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 'for' statement may indicate programmer error
+ // 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 conditional operator may indicate programmer error
+ // 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 conditional operator may indicate programmer error
+ // 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 conditional operator may indicate programmer error
+ // 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 conditional operator may indicate programmer error
+ // 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));
@@ -70,43 +72,42 @@ void test_conditional(int a, int b, int c, int d) {
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
+ // 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
+ // 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
+ // 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
+ // 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
+ // 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
+ // 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
+ // 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 'while' statement may indicate programmer error
+ // 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 'for' statement may indicate programmer error
+ // 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 'do' statement may indicate programmer error
+ // 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
+ // 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
+ // 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 conditional operator may indicate programmer error
+ // 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) {
@@ -121,4 +122,6 @@ void test_no_warning(int a, int b, int c) {
int arr[10] = {0};
if (f(a = b)) {}
if (arr[c = a]) {};
+
+ switch (a = b) {}
}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/assignment-in-selection-statement.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/assignment-in-selection-statement.cpp
new file mode 100644
index 0000000000000..6a2d76a3b0d50
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/assignment-in-selection-statement.cpp
@@ -0,0 +1,41 @@
+// RUN: %check_clang_tidy %s -std=c++17 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);
+}
More information about the cfe-commits
mailing list