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?= <[email protected]> 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?= <[email protected]> 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?= <[email protected]> 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); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
