[clang-tools-extra] Add bugprone-loop-variable-copied-then-modified clang-tidy check. (PR #157213)
Piotr Zegar via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 16 14:37:28 PDT 2025
================
@@ -0,0 +1,152 @@
+// RUN: %check_clang_tidy %s bugprone-loop-variable-copied-then-modified %t --fix-notes
+
+template <typename T>
+struct Iterator {
+ void operator++() {}
+ const T& operator*() {
+ static T* TT = new T();
+ return *TT;
+ }
+ bool operator!=(const Iterator &) { return false; }
+};
+template <typename T>
+struct View {
+ T begin() { return T(); }
+ T begin() const { return T(); }
+ T end() { return T(); }
+ T end() const { return T(); }
+};
+
+struct ConstructorConvertible {
+};
+
+struct S {
+ int value;
+
+ S() : value(0) {};
+ S(const S &);
+ S(const ConstructorConvertible&) {}
+ ~S();
+ S &operator=(const S &);
+ void modify() {
+ value++;
+ }
+};
+
+struct Convertible {
+ operator S() const {
+ return S();
+ }
+};
+
+struct PairLike {
+ int id;
+ S data;
+};
+
+template <typename V>
+struct Generic {
+ V value;
+
+ Generic() : value{} {};
+ Generic(const Generic &);
+ ~Generic();
+ Generic &operator=(const Generic &);
+ void modify() {
+ value++;
+ }
+};
+
+#define LOOP_AND_MODIFY(VAR_NAME, TYPE) \
+ for (TYPE VAR_NAME : View<Iterator<TYPE>>()) { \
+ VAR_NAME.modify(); \
+ }
+
+void NegativeLoopVariableNotCopied() {
+ for (const S& S1 : View<Iterator<S>>()) {
+ // It's fine to copy-by-value S1 into some other S.
+ S S2 = S1;
+ }
+}
+
+void NegativeLoopVariableCopiedButNotModified() {
+ for (S S1 : View<Iterator<S>>()) {
+ }
+}
+
+void PositiveLoopVariableCopiedAndThenModfied() {
+ for (S S1 : View<Iterator<S>>()) {
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: loop variable 'S1' is copied and then (possibly) modified; use an explicit copy inside the body of the loop or make the variable a reference
+ // CHECK-MESSAGES: [[@LINE-2]]:10: note: consider making 'S1' a reference
+ // CHECK-FIXES: for (const S& S1 : View<Iterator<S>>()) {
+ S1.modify();
+ }
+}
+
+void PositiveLoopVariableCopiedAndThenModifiedAuto() {
+ for (auto S1 : View<Iterator<S>>()) {
+ // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable 'S1' is copied and then (possibly) modified; use an explicit copy inside the body of the loop or make the variable a reference
+ // CHECK-MESSAGES: [[@LINE-2]]:13: note: consider making 'S1' a reference
+ // CHECK-FIXES: for (const auto& S1 : View<Iterator<S>>()) {
+ S1.modify();
+ }
+}
+
+void PositiveLoopVariableIsTypedefType() {
+ typedef int TypedefInt;
+ for (TypedefInt ti : View<Iterator<TypedefInt>>()) {
+ // CHECK-MESSAGES: [[@LINE-1]]:19: warning: loop variable 'ti' is copied and then (possibly) modified; use an explicit copy inside the body of the loop or make the variable a reference
+ // CHECK-MESSAGES: [[@LINE-2]]:19: note: consider making 'ti' a reference
+ // CHECK-FIXES: for (const TypedefInt& ti : View<Iterator<TypedefInt>>()) {
+ ti += 1;
+ }
+}
+
+void NegativeLoopVariableIsInsideMacro() {
+ LOOP_AND_MODIFY(S1, S);
+}
+
+template <typename T>
+struct ValueReturningIterator {
+ void operator++() {}
+ T operator*() { return T(); }
+ bool operator!=(const ValueReturningIterator &) { return false; }
+ typedef const T &const_reference;
+};
+
+void NegativeValueIterator() {
+ // Check does not trigger for iterators that return elements by value.
+ for (S S1 : View<ValueReturningIterator<S>>()) {
+ S1.modify();
+ }
+}
+
+void NegativeConstructedByConversion() {
+ Convertible C[0];
+ for (S S1 : C) {
+ S1.modify();
+ }
+}
+
+void NegativeNotConstructedByCopy() {
+ // Designed to exercise the unless(NotConstructedByCopy) clause in the check's source code.
+ // Distinct from NegativeConstructedByConversion because it exercises a converting constructor rather than a conversion operator.
+ ConstructorConvertible C[0];
+ for (S S1 : C) {
+ S1.modify();
+ }
+}
+
+void PositiveLoopVariableIsStructuredBinding() {
+ for (auto [id, data] : View<Iterator<PairLike>>()) {
+ // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable '' is copied and then (possibly) modified; use an explicit copy inside the body of the loop or make the variable a reference
----------------
PiotrZSL wrote:
this is because you catching here DecompositionDecl, you need to check it properly
https://github.com/llvm/llvm-project/pull/157213
More information about the cfe-commits
mailing list